diff --git a/aegisub/Makefile.target b/aegisub/Makefile.target
index faca12b97..cedeb5b77 100644
--- a/aegisub/Makefile.target
+++ b/aegisub/Makefile.target
@@ -30,7 +30,7 @@ ifdef LIB_SHARED
LIB_SHARED_FULL = $(LIB_SHARED).$(LIB_VERSION)
$(LIB_SHARED) : $(OBJ)
$(BIN_CXX) $(LIB_SHARED_LINK) $(LDFLAGS) $(OBJ) -o $(LIB_SHARED_FULL)
- $(BIN_LN) -s $(LIB_SHARED_FULL) $(LIB_SHARED)
+ $(BIN_LN) -sf $(LIB_SHARED_FULL) $(LIB_SHARED)
CLEANFILES+= $(LIB_SHARED_FULL)
endif
diff --git a/aegisub/docs/doxygen/doxyfile_base b/aegisub/docs/doxygen/doxyfile_base
index 704d97afa..64e241d61 100644
--- a/aegisub/docs/doxygen/doxyfile_base
+++ b/aegisub/docs/doxygen/doxyfile_base
@@ -12,7 +12,7 @@ OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
-ALWAYS_DETAILED_SEC = YES
+ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH = $(SRC_TRIM)
@@ -48,7 +48,7 @@ EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = YES
-EXTRACT_ANON_NSPACES = YES
+EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
@@ -56,12 +56,12 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
-SHOW_INCLUDE_FILES = YES
+SHOW_INCLUDE_FILES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_GROUP_NAMES = NO
-SORT_BY_SCOPE_NAME = NO
+SORT_BY_SCOPE_NAME = YES
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
@@ -111,7 +111,7 @@ INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
# FIXME: set these two to NO?
REFERENCED_BY_RELATION = YES
-REFERENCES_RELATION = YES
+REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
@@ -216,7 +216,12 @@ INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
#PREDEFINED =
PREDEFINED += WXUNUSED(x)=
-EXPAND_AS_DEFINED =
+PREDEFINED += "CMD_NAME(a)=const char* name() { return a; } ///< a"
+PREDEFINED += "STR_MENU(a)=wxString StrMenu() const { return a; } ///< a"
+PREDEFINED += "STR_DISP(a)=wxString StrDisplay() const { return a; } ///< a"
+PREDEFINED += "STR_HELP(a)=wxString StrHelp() const { return a; } ///< a"
+
+#EXPAND_AS_DEFINED = CMD_NAME STR_MENU STR_DISP STR_HELP
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
@@ -260,3 +265,4 @@ DOT_CLEANUP = YES
# Options related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO
+SERVER_BASED_SEARCH = YES
diff --git a/aegisub/docs/doxygen/pages_aegisub/index.dox b/aegisub/docs/doxygen/pages_aegisub/index.dox
index 3ae3cc409..e309597ce 100644
--- a/aegisub/docs/doxygen/pages_aegisub/index.dox
+++ b/aegisub/docs/doxygen/pages_aegisub/index.dox
@@ -1,5 +1,8 @@
/** @mainpage
+
Source Groups
+
+
Main
- @ref main
- @ref main_headers
@@ -24,6 +27,8 @@ Dialogues
- @ref thesaurus
- @ref visual_ts
+ |
+
Miscellanous
- @ref build
- @ref tools_ui
@@ -46,4 +51,26 @@ Video
- @ref video_input
- @ref video_output
+ |
+
+
+
+
+Available Command List
+ - @ref cmd-app
+ - @ref cmd-audio
+ - @ref cmd-am
+ - @ref cmd-edit
+ - @ref cmd-grid
+ - @ref cmd-help
+ - @ref cmd-keyframed
+ - @ref cmd-menu
+ - @ref cmd-recent
+ - @ref cmd-subtitle
+ - @ref cmd-time
+ - @ref cmd-timecode
+ - @ref cmd-tool
+ - @ref cmd-video
+
+
*/
diff --git a/aegisub/libaegisub/Makefile b/aegisub/libaegisub/Makefile
index fe87afa85..1e9e7d5da 100644
--- a/aegisub/libaegisub/Makefile
+++ b/aegisub/libaegisub/Makefile
@@ -21,6 +21,8 @@ SRC = \
common/charset.cpp \
common/charset_conv.cpp \
common/charset_ucd.cpp \
+ common/hotkey.cpp \
+ common/json.cpp \
common/mru.cpp \
common/option.cpp \
common/option_visit.cpp \
diff --git a/aegisub/libaegisub/common/hotkey.cpp b/aegisub/libaegisub/common/hotkey.cpp
new file mode 100644
index 000000000..a0cfd68fe
--- /dev/null
+++ b/aegisub/libaegisub/common/hotkey.cpp
@@ -0,0 +1,196 @@
+// Copyright (c) 2010, Amar Takhar
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// $Id$
+
+/// @file hotkey.cpp
+/// @brief Hotkey handler
+/// @ingroup hotkey menu event window
+
+#include "config.h"
+
+#ifndef LAGI_PRE
+#include
+
+#include
+#endif
+
+#include "libaegisub/access.h"
+#include "libaegisub/io.h"
+#include "libaegisub/json.h"
+#include "libaegisub/log.h"
+#include "libaegisub/hotkey.h"
+
+namespace agi {
+ namespace hotkey {
+
+Hotkey *hotkey;
+
+std::string Combo::Str() {
+ std::string str(key_map[0]);
+ for (unsigned int i=1; i < key_map.size(); i++) {
+ str.append("-" + key_map[i]);
+ }
+ return str;
+}
+
+std::string Combo::StrMenu() {
+ return Str();
+}
+
+void Hotkey::ComboInsert(Combo *combo) {
+ map.insert(HotkeyMapPair(combo->Str(), combo));
+}
+
+Hotkey::~Hotkey() {
+ Flush();
+}
+
+Hotkey::Hotkey(const std::string &file, const std::string &default_config):
+ config_file(file), config_default(default_config) {
+
+ LOG_D("hotkey/init") << "Generating hotkeys.";
+
+ std::istream *stream;
+
+ try {
+ stream = agi::io::Open(config_file);
+ } catch (const acs::AcsNotFound&) {
+stream = new std::istringstream(config_default);
+ }
+
+
+ json::UnknownElement hotkey_root;
+ try {
+ hotkey_root = agi::json_util::parse(stream);
+ } catch (json::Reader::ParseException& e) {
+ // There's definatly a better way to do this.
+ std::istringstream *stream = new std::istringstream(config_default);
+ hotkey_root = agi::json_util::parse(stream);
+ }
+
+ json::Object object = hotkey_root;
+
+
+ for (json::Object::const_iterator index(object.Begin()); index != object.End(); index++) {
+ const json::Object::Member& member = *index;
+ const json::Object& obj = member.element;
+ BuildHotkey(member.name, obj);
+ }
+}
+
+
+void Hotkey::BuildHotkey(std::string context, const json::Object& object) {
+
+ for (json::Object::const_iterator index(object.Begin()); index != object.End(); index++) {
+ const json::Object::Member& member = *index;
+
+
+ const json::Array& array = member.element;
+ for (json::Array::const_iterator arr_index(array.Begin()); arr_index != array.End(); arr_index++) {
+
+ Combo *combo = new Combo(context, member.name);
+
+ const json::Object& obj = *arr_index;
+
+ const json::Array& arr_mod = obj["modifiers"];
+
+ if (arr_mod.Size() > 0) {
+ for (json::Array::const_iterator arr_mod_index(arr_mod.Begin()); arr_mod_index != arr_mod.End(); arr_mod_index++) {
+ const json::String& key_mod = *arr_mod_index;
+ combo->KeyInsert(key_mod.Value());
+ } // for arr_mod_index
+
+ }
+ const json::String& key = obj["key"];
+ combo->KeyInsert(key.Value());
+
+ const json::Boolean& enable = obj["enable"];
+ combo->Enable(enable);
+
+ ComboInsert(combo);
+ } // for arr_index
+ } // for index
+}
+
+
+bool Hotkey::Scan(const std::string context, const std::string str, std::string &cmd) {
+ HotkeyMap::iterator index;
+ std::pair range;
+
+ range = map.equal_range(str);
+ std::string local, dfault;
+
+
+ for (index = range.first; index != range.second; ++index) {
+
+ std::string ctext = (*index).second->Context();
+
+ if (ctext == "Always") {
+ cmd = (*index).second->CmdName();
+ LOG_D("agi/hotkey/found") << "Found: " << str << " Context (req/found): " << context << "/Always Command: " << cmd;
+ return 0;
+ } else if (ctext == "Default") {
+ dfault = (*index).second->CmdName();
+ } else if (ctext == context) {
+ local = (*index).second->CmdName();
+ }
+ }
+
+ if (!local.empty()) {
+ cmd = local;
+ LOG_D("agi/hotkey/found") << "Found: " << str << " Context: " << context << " Command: " << local;
+ return 0;
+ } else if (!dfault.empty()) {
+ cmd = dfault;
+ LOG_D("agi/hotkey/found") << "Found: " << str << " Context (req/found): " << context << "/Default Command: " << dfault;
+ return 0;
+ }
+
+ return 1;
+
+}
+
+void Hotkey::Flush() {
+
+ json::Object root;
+
+ HotkeyMap::iterator index;
+ for (index = map.begin(); index != map.end(); ++index) {
+
+ Combo::ComboMap combo_map(index->second->Get());
+
+ json::Array modifiers;
+ for (int i = 0; i != combo_map.size()-1; i++) {
+ modifiers.Insert(json::String(combo_map[i]));
+ }
+
+ json::Object hotkey;
+ hotkey["modifiers"] = modifiers;
+ hotkey["key"] = json::String(combo_map.back());
+ hotkey["enable"] = json::Boolean(index->second->IsEnabled());
+
+ json::Object& context_obj = root[index->second->Context()];
+ json::Array& combo_array = context_obj[index->second->CmdName()];
+
+ combo_array.Insert(hotkey);
+ }
+
+ io::Save file(config_file);
+ json::Writer::Write(root, file.Get());
+
+}
+
+ } // namespace toolbar
+} // namespace agi
diff --git a/aegisub/libaegisub/common/json.cpp b/aegisub/libaegisub/common/json.cpp
new file mode 100644
index 000000000..c78574ec1
--- /dev/null
+++ b/aegisub/libaegisub/common/json.cpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2010, Amar Takhar
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// $Id$
+
+/// @file json.cpp
+/// @brief Parse JSON files and return json::UnknownElement
+/// @ingroup libaegisub io
+
+
+#ifdef LAGI_PRE
+#include
+#include
+#endif
+
+#include "libaegisub/access.h"
+#include "libaegisub/io.h"
+#include "libaegisub/json.h"
+
+
+namespace agi {
+ namespace json_util {
+
+json::UnknownElement parse(std::istream *stream) {
+ json::UnknownElement root;
+
+ try {
+ json::Reader::Read(root, *stream);
+ } catch (json::Reader::ParseException& e) {
+ std::cout << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1 << std::endl << std::endl;
+ } catch (json::Exception& e) {
+ /// @todo Do something better here, maybe print the exact error
+ std::cout << "json::Exception: " << e.what() << std::endl;
+ }
+
+ delete stream;
+ return root;
+}
+
+
+json::UnknownElement file(const std::string file) {
+ return parse(io::Open(file));
+}
+
+
+json::UnknownElement file(const std::string file, const std::string &default_config) {
+
+ try {
+ return parse(io::Open(file));
+
+ // We only want to catch this single error as anything else could
+ // reflect a deeper problem. ie, failed i/o, wrong permissions etc.
+ } catch (const acs::AcsNotFound&) {
+
+ std::istringstream stream(default_config);
+ return parse(&stream);
+ }
+}
+
+
+ } // namespace json_util
+} // namespace agi
diff --git a/aegisub/libaegisub/common/log.cpp b/aegisub/libaegisub/common/log.cpp
index 4f30d38bc..968eab747 100644
--- a/aegisub/libaegisub/common/log.cpp
+++ b/aegisub/libaegisub/common/log.cpp
@@ -27,6 +27,9 @@
#include
#endif
+#include "libaegisub/cajun/elements.h"
+#include "libaegisub/cajun/writer.h"
+#include "libaegisub/io.h"
#include "libaegisub/log.h"
#include "libaegisub/mutex.h"
#include "libaegisub/types.h"
@@ -36,7 +39,7 @@ namespace agi {
namespace log {
/// Global log sink.
-std::auto_ptr log(new LogSink());
+LogSink *log;
/// Short Severity ID
/// Keep this ordered the same as Severity
@@ -61,11 +64,51 @@ SinkMessage::~SinkMessage() {
}
-LogSink::LogSink() {
+LogSink::LogSink(const std::string dir_log): dir_log(dir_log) {
+ util::time_log(time_start);
}
+/// @todo The log files need to be trimed after N amount.
LogSink::~LogSink() {
-/// @todo This needs to flush all log data to disk on quit.
+ json::Object root;
+ json::Array array;
+
+ agi_timeval time_close;
+ util::time_log(time_close);
+
+ std::stringstream str;
+ str << dir_log << time_start.tv_sec << ".json";
+ io::Save file(str.str());
+
+ for (unsigned int i=0; i < sink.size(); i++) {
+ json::Object entry;
+ entry["sec"] = json::Number(sink[i]->tv.tv_sec);
+ entry["usec"] = json::Number(sink[i]->tv.tv_usec);
+ entry["severity"] = json::Number(sink[i]->severity),
+ entry["section"] = json::String(sink[i]->section);
+ entry["file"] = json::String(sink[i]->file);
+ entry["func"] = json::String(sink[i]->func);
+ entry["line"] = json::Number(sink[i]->line);
+ entry["message"] = json::String(std::string(sink[i]->message, sink[i]->len));
+
+ array.Insert(entry);
+ }
+
+ json::Array timeval_open;
+ timeval_open.Insert(json::Number(time_start.tv_sec));
+ timeval_open.Insert(json::Number(time_start.tv_usec));
+ root["timeval"]["open"] = timeval_open;
+
+ json::Array timeval_close;
+ timeval_close.Insert(json::Number(time_close.tv_sec));
+ timeval_close.Insert(json::Number(time_close.tv_usec));
+ root["timeval"]["close"] = timeval_close;
+
+
+ root["log"] = array;
+
+ json::Writer::Write(root, file.Get());
+
agi::util::delete_clear(sink);
}
diff --git a/aegisub/libaegisub/include/libaegisub/charset.h b/aegisub/libaegisub/include/libaegisub/charset.h
index fca82fa68..83fe98507 100644
--- a/aegisub/libaegisub/include/libaegisub/charset.h
+++ b/aegisub/libaegisub/include/libaegisub/charset.h
@@ -27,6 +27,7 @@
#include
namespace agi {
+ /// Character set conversion and detection.
namespace charset {
DEFINE_BASE_EXCEPTION_NOINNER(CharsetError, agi::Exception)
diff --git a/aegisub/libaegisub/include/libaegisub/exception.h b/aegisub/libaegisub/include/libaegisub/exception.h
index 1353244f2..b6b28b355 100644
--- a/aegisub/libaegisub/include/libaegisub/exception.h
+++ b/aegisub/libaegisub/include/libaegisub/exception.h
@@ -63,11 +63,11 @@ namespace agi {
/// When throwing exceptions, throw temporaries, not heap allocated
/// objects. (C++ FAQ Lite 17.6.) I.e. this is correct:
/// @code
- /// throw Aegisub::SomeException("Message for exception");
+ /// throw agi::SomeException("Message for exception");
/// @endcode
/// This is wrong:
/// @code
- /// throw new Aegisub::SomeException("Remember this is the wrong way!");
+ /// throw new agi::SomeException("Remember this is the wrong way!");
/// @endcode
/// Exceptions must not be allocated on heap, because of the risks of
/// leaking memory that way. (C++ FAQ Lite 17.8.)
@@ -81,10 +81,10 @@ namespace agi {
/// try {
/// /* ... */
/// }
- /// catch (Aegisub::UserCancelException &e) {
+ /// catch (agi::UserCancelException &e) {
/// /* handle the fact that the user cancelled */
/// }
- /// catch (Aegisub::VideoInputException &e) {
+ /// catch (agi::VideoInputException &e) {
/// /* handle the video provider failing */
/// }
/// @endcode
@@ -238,8 +238,8 @@ namespace agi {
};
- /// @class Aegisub::UserCancelException
- /// @extends Aegisub::Exception
+ /// @class agi::UserCancelException
+ /// @extends agi::Exception
/// @brief Exception for "user cancel" events
///
/// I.e. when we want to abort an operation because the user requested that we do so.
@@ -253,8 +253,8 @@ namespace agi {
DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel")
- /// @class Aegisub::InternalError
- /// @extends Aegisub::Exception
+ /// @class agi::InternalError
+ /// @extends agi:Exception
/// @brief Errors that should never happen and point to some invalid assumption in the code
///
/// Throw an internal error when a sanity check fails, and the insanity should have
@@ -265,8 +265,8 @@ namespace agi {
DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error")
- /// @class Aegisub::FileSystemError
- /// @extends Aegisub::Exception
+ /// @class agi::FileSystemError
+ /// @extends agi::Exception
/// @brief Base class for errors related to the file system
///
/// This base class can not be instantiated.
@@ -274,8 +274,8 @@ namespace agi {
/// causes for errors.
DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError,Exception)
- /// @class Aegisub::FileNotAccessibleError
- /// @extends Aegisub::FileSystemError
+ /// @class agi::FileNotAccessibleError
+ /// @extends agi::FileSystemError
/// @brief A file can't be accessed for some reason
DEFINE_SIMPLE_EXCEPTION_NOINNER(FileNotAccessibleError,FileSystemError,"filesystem/not_accessible")
@@ -289,16 +289,16 @@ namespace agi {
/// @param filename Name of the file that could not be found
FileNotFoundError(const std::string &filename) : FileNotAccessibleError(std::string("File not found: ") + filename) { }
- // Not documented, see Aegisub::Exception class
+ // Not documented, see agi::Exception class
const char * GetName() const { return "filesystem/not_accessible/not_found"; }
- // Not documented, see Aegisub::Exception class
+ // Not documented, see agi::Exception class
Exception * Copy() const { return new FileNotFoundError(*this); }
};
- /// @class Aegisub::InvalidInputException
- /// @extends Aegisub::Exception
+ /// @class agi::InvalidInputException
+ /// @extends agi::Exception
/// @brief Some input data were invalid and could not be processed
DEFINE_BASE_EXCEPTION(InvalidInputException,Exception)
diff --git a/aegisub/libaegisub/include/libaegisub/hotkey.h b/aegisub/libaegisub/include/libaegisub/hotkey.h
new file mode 100644
index 000000000..a274c5e91
--- /dev/null
+++ b/aegisub/libaegisub/include/libaegisub/hotkey.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2010, Amar Takhar
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// $Id$
+
+/// @file hotkey.h
+/// @brief Hotkey handler
+/// @ingroup hotkey menu event window
+
+#include "config.h"
+
+#ifndef LAGI_PRE
+#include
+
+#include
+#endif
+
+#include
+
+
+namespace agi {
+ namespace hotkey {
+
+
+class Hotkey;
+/// Hotkey instance.
+extern Hotkey *hotkey;
+
+/// @class Combo
+/// A Combo represents a linear sequence of characters set in an std::vector. This makes up
+/// a single combination, or "Hotkey".
+class Combo {
+friend class Hotkey;
+
+public:
+ /// Linear key sequence that forms a combination or "Combo"
+ typedef std::vector ComboMap;
+
+ /// Constructor
+ /// @param ctx Context
+ /// @param cmd Command name
+ Combo(std::string ctx, std::string cmd): cmd_name(cmd), context(ctx) {}
+
+ /// Destructor
+ ~Combo() {}
+
+ /// String representation of the Combo
+ std::string Str();
+
+ /// String suitable for usage in a menu.
+ std::string StrMenu();
+
+ /// Get the literal combo map.
+ /// @return ComboMap (std::vector) of linear key sequence.
+ const ComboMap& Get() { return key_map; }
+
+ /// Command name triggered by the combination.
+ /// @return Command name
+ const std::string& CmdName() { return cmd_name; }
+
+ /// Context this Combo is triggered in.
+ const std::string& Context() { return context; }
+
+ /// Enable or disable Combo or "Hotkey".
+ /// @param e Bool state.
+ void Enable(bool e) { enable = e; }
+
+ /// Check whether Combo is currently enabled or disabled.
+ /// @return State.
+ const bool& IsEnabled() { return enable; }
+
+private:
+ ComboMap key_map; ///< Map.
+ const std::string cmd_name; ///< Command name.
+ const std::string context; ///< Context
+ bool enable; ///< Enable/Disable state
+
+ /// Insert a key into the ComboMap.
+ /// @param key Key to insert.
+ void KeyInsert(std::string key) { key_map.push_back(key); }
+};
+
+
+/// @class Hotkey
+/// Holds the map of Combo instances and handles searching for matching key sequences.
+class Hotkey {
+public:
+ /// Constructor
+ /// @param file Location of user config file.
+ /// @param default_config Default config.
+ Hotkey(const std::string &file, const std::string &default_config);
+
+ /// Destructor.
+ ~Hotkey();
+
+ /// Scan for a matching key.
+ /// @param context Context requested.
+ /// @param str Hyphen seperated key squence.
+ /// @param[out] cmd Command found.
+ bool Scan(const std::string context, const std::string str, std::string &cmd);
+
+private:
+ typedef std::multimap HotkeyMap; ///< Map to hold Combo instances.
+ typedef std::pair HotkeyMapPair; ///< Pair for HotkeyMap.
+ HotkeyMap map; ///< HotkeyMap Instance.
+ const std::string config_file; ///< Default user config location.
+ const std::string config_default; ///< Default config.
+
+ /// Build hotkey map.
+ /// @param context Context being parsed.
+ /// @param object json::Object holding items for context being parsed.
+ void BuildHotkey(std::string context, const ::json::Object& object);
+
+ /// Insert Combo into HotkeyMap instance.
+ /// @param combo Combo to insert.
+ void ComboInsert(Combo *combo);
+
+ /// Write active Hotkey configuration to disk.
+ void Flush();
+};
+
+ } // namespace hotkey
+} // namespace agi
diff --git a/aegisub/libaegisub/include/libaegisub/json.h b/aegisub/libaegisub/include/libaegisub/json.h
new file mode 100644
index 000000000..e2f5623a1
--- /dev/null
+++ b/aegisub/libaegisub/include/libaegisub/json.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2010, Amar Takhar
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// $Id$
+
+/// @file json.h
+/// @brief Parse JSON files and return json::UnknownElement
+/// @ingroup libaegisub io
+
+
+#include
+#include
+
+namespace agi {
+ namespace json_util {
+
+/// Parse a JSON stream.
+/// @param stream JSON stream, this is deleted internally.
+/// @return json::UnknownElement
+json::UnknownElement parse(std::istream *stream);
+
+/// Parse a JSON file.
+/// @param file Path JSON to file
+/// @return json::UnknownElement
+json::UnknownElement file(const std::string file);
+
+/// Parse a json stream, with default handler.
+/// @param file Path to JSON file.
+/// @param Default config file to load incase of nonexistent file
+/// @return json::UnknownElement
+json::UnknownElement file(const std::string file, const std::string &default_config);
+
+
+ } // namespace json_util
+} // namespace agi
diff --git a/aegisub/libaegisub/include/libaegisub/log.h b/aegisub/libaegisub/include/libaegisub/log.h
index 7c579b752..26d7c53b5 100644
--- a/aegisub/libaegisub/include/libaegisub/log.h
+++ b/aegisub/libaegisub/include/libaegisub/log.h
@@ -68,7 +68,7 @@ enum Severity {
extern const char *Severity_ID;
/// Global log sink.
-extern std::auto_ptr log;
+extern LogSink *log;
/// Container to hold a single message
struct SinkMessage {
@@ -111,9 +111,17 @@ class LogSink {
/// List of function pointers to emitters
std::vector emitters;
+ /// Init time for log writing purposes.
+ agi_timeval time_start;
+
+ /// Directory to place logfiles.
+ const std::string dir_log;
+
+
public:
/// Constructor
- LogSink();
+ /// @param dir_log Directory to place log files.
+ LogSink(const std::string dir_log);
/// Destructor
~LogSink();
diff --git a/aegisub/libaegisub/include/libaegisub/mru.h b/aegisub/libaegisub/include/libaegisub/mru.h
index 887486ef4..2e363c3db 100644
--- a/aegisub/libaegisub/include/libaegisub/mru.h
+++ b/aegisub/libaegisub/include/libaegisub/mru.h
@@ -97,7 +97,7 @@ private:
/// Internal MRUMap values.
MRUMap mru;
- void Load(const std::string &key, const json::Array& array);
+ void Load(const std::string &key, const ::json::Array& array);
inline void Prune(MRUListMap& map);
};
diff --git a/aegisub/libaegisub/include/libaegisub/option.h b/aegisub/libaegisub/include/libaegisub/option.h
index 1c4f81387..77919f3be 100644
--- a/aegisub/libaegisub/include/libaegisub/option.h
+++ b/aegisub/libaegisub/include/libaegisub/option.h
@@ -65,7 +65,7 @@ private:
/// @brief Create option object.
/// @param path Path to store
- json::Object CreateObject(std::string path);
+ ::json::Object CreateObject(std::string path);
/// User config (file that will be written to disk)
const std::string config_file;
@@ -89,7 +89,7 @@ protected:
/// @param[out] obj Parent object
/// @param[in] path Path option should be stored in.
/// @param[in] value Value to write.
- static bool PutOption(json::Object &obj, const std::string &path, const json::UnknownElement &value);
+ static bool PutOption(::json::Object &obj, const std::string &path, const ::json::UnknownElement &value);
public:
diff --git a/aegisub/libaegisub/include/libaegisub/scoped_ptr.h b/aegisub/libaegisub/include/libaegisub/scoped_ptr.h
index c0d25e209..44eb9d1fc 100644
--- a/aegisub/libaegisub/include/libaegisub/scoped_ptr.h
+++ b/aegisub/libaegisub/include/libaegisub/scoped_ptr.h
@@ -12,7 +12,7 @@
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
-// $Id$
+// $Id: scoped_ptr.h 153 2010-12-08 14:45:38Z verm $
/// @file scoped_ptr.h
/// @brief
diff --git a/aegisub/libaegisub/include/libaegisub/vfr.h b/aegisub/libaegisub/include/libaegisub/vfr.h
index 2aff1d1e5..0574cf269 100644
--- a/aegisub/libaegisub/include/libaegisub/vfr.h
+++ b/aegisub/libaegisub/include/libaegisub/vfr.h
@@ -28,7 +28,8 @@
#include
namespace agi {
-namespace vfr {
+ /// Framerate handling.
+ namespace vfr {
enum Time {
/// Use the actual frame times
@@ -134,5 +135,5 @@ public:
double FPS() const { return fps; }
};
-}
-}
+ } // namespace vfr
+} // namespace agi
diff --git a/aegisub/po/make_pot.bat b/aegisub/po/make_pot.bat
new file mode 100644
index 000000000..6bf402791
--- /dev/null
+++ b/aegisub/po/make_pot.bat
@@ -0,0 +1,4 @@
+del /s list.txt
+dir /w /b ..\aegisub\*.cpp ..\aegisub\*.h >> list.txt
+"C:\Program Files (x86)\GnuWin32\bin\xgettext.exe" --files-from=list.txt --directory=../aegisub/ --output=aegisub.pot --c++ -k_
+pause
\ No newline at end of file
diff --git a/aegisub/src/Makefile b/aegisub/src/Makefile
index 7299a5b62..07f8ab542 100644
--- a/aegisub/src/Makefile
+++ b/aegisub/src/Makefile
@@ -5,14 +5,14 @@ PROGRAM_INSTALL = yes
PRECOMPILED_HEADER_NAME=agi_pre.h
-SUBDIRS = libresrc
+SUBDIRS = command libresrc
-CXXFLAGS += -DAEGISUB -I. -I.. -Iinclude -I../libaegisub/include $(CPPFLAGS_WX)
+CXXFLAGS += -DAEGISUB -I. -I.. -Iinclude -I../libaegisub/include $(CFLAGS_PTHREAD) $(CPPFLAGS_WX)
LDFLAGS += -L../libaegisub -laegisub-3.0
LDFLAGS += $(CFLAGS_DEBUG) $(CFLAGS_PROFILE) $(LDFLAGS_CCMALLOC) $(LDFLAGS_EFENCE)
LDFLAGS += $(LDFLAGS_GL) $(LDFLAGS_PTHREAD) $(LDFLAGS_WX) $(LDFLAGS_FREETYPE) $(LDFLAGS_FONTCONFIG)
-LDFLAGS_POST += libresrc/libresrc.a $(LDFLAGS_UCHARDET)
+LDFLAGS_POST += libresrc/libresrc.a $(LDFLAGS_UCHARDET) command/aegisub_command.a
ifdef BUILD_DARWIN
LDFLAGS += -lz
@@ -188,13 +188,13 @@ SRC += \
font_file_lister.cpp \
font_file_lister_fontconfig.cpp \
frame_main.cpp \
- frame_main_events.cpp \
gl_text.cpp \
gl_wrap.cpp \
help_button.cpp \
- hotkeys.cpp \
+ hotkey.cpp \
kana_table.cpp \
main.cpp \
+ menu.cpp \
md5.c \
mkv_wrap.cpp \
mythes.cxx \
@@ -230,6 +230,7 @@ SRC += \
timeedit_ctrl.cpp \
threaded_frame_source.cpp \
toggle_bitmap.cpp \
+ toolbar.cpp \
tooltip_manager.cpp \
utils.cpp \
validators.cpp \
diff --git a/aegisub/src/audio_box.cpp b/aegisub/src/audio_box.cpp
index 59f1b6c9f..2a40f365f 100644
--- a/aegisub/src/audio_box.cpp
+++ b/aegisub/src/audio_box.cpp
@@ -55,7 +55,6 @@
#include "audio_karaoke.h"
#include "audio_timing.h"
#include "frame_main.h"
-#include "hotkeys.h"
#include "include/aegisub/audio_player.h"
#include "libresrc/libresrc.h"
#include "main.h"
diff --git a/aegisub/src/audio_controller.h b/aegisub/src/audio_controller.h
index 81d7adfa4..aa016347e 100644
--- a/aegisub/src/audio_controller.h
+++ b/aegisub/src/audio_controller.h
@@ -33,6 +33,7 @@
/// @see audio_controller.cpp
/// @ingroup audio_ui
+#pragma once
#ifndef AGI_PRE
#include
diff --git a/aegisub/src/base_grid.cpp b/aegisub/src/base_grid.cpp
index f8551fcb1..6d3a89801 100644
--- a/aegisub/src/base_grid.cpp
+++ b/aegisub/src/base_grid.cpp
@@ -42,9 +42,12 @@
#include
#include
+#include
#include
#endif
+#include "aegisub/hotkey.h"
+
#include "base_grid.h"
#include "ass_dialogue.h"
@@ -518,7 +521,7 @@ BEGIN_EVENT_TABLE(BaseGrid,wxWindow)
EVT_SIZE(BaseGrid::OnSize)
EVT_COMMAND_SCROLL(GRID_SCROLLBAR,BaseGrid::OnScroll)
EVT_MOUSE_EVENTS(BaseGrid::OnMouseEvent)
- EVT_KEY_DOWN(BaseGrid::OnKeyPress)
+ EVT_KEY_DOWN(BaseGrid::OnKeyDown)
END_EVENT_TABLE()
@@ -1142,11 +1145,15 @@ bool BaseGrid::IsDisplayed(AssDialogue *line) {
/// @param event
/// @return
///
-void BaseGrid::OnKeyPress(wxKeyEvent &event) {
+void BaseGrid::OnKeyDown(wxKeyEvent &event) {
// Get size
int w,h;
GetClientSize(&w,&h);
+ hotkey::check("Subtitle Grid", event.GetKeyCode(), event.GetUnicodeKey(), event.GetModifiers());
+ event.StopPropagation();
+
+
// Get scan code
int key = event.GetKeyCode();
#ifdef __APPLE__
diff --git a/aegisub/src/base_grid.h b/aegisub/src/base_grid.h
index c323a03fe..fb96b86b4 100644
--- a/aegisub/src/base_grid.h
+++ b/aegisub/src/base_grid.h
@@ -93,7 +93,7 @@ class BaseGrid : public wxWindow, public BaseSelectionController {
void OnSize(wxSizeEvent &event);
void OnScroll(wxScrollEvent &event);
void OnMouseEvent(wxMouseEvent &event);
- void OnKeyPress(wxKeyEvent &event);
+ void OnKeyDown(wxKeyEvent &event);
void DrawImage(wxDC &dc);
diff --git a/aegisub/src/command/Makefile b/aegisub/src/command/Makefile
new file mode 100644
index 000000000..0f0111c3a
--- /dev/null
+++ b/aegisub/src/command/Makefile
@@ -0,0 +1,34 @@
+include ../../Makefile.inc
+
+CXXFLAGS += -I../../ -I.. -I. -I../../libaegisub/include -I../include -DAEGISUB
+
+CXXFLAGS += $(CFLAGS_WX)
+PRECOMPILED_HEADER_NAME=../agi_pre.h
+
+
+LIB = aegisub_command.a
+
+SRC = \
+ app.cpp \
+ audio.cpp \
+ automation.cpp \
+ edit.cpp \
+ grid.cpp \
+ help.cpp \
+ keyframe.cpp \
+ medusa.cpp \
+ menu.cpp \
+ recent.cpp \
+ subtitle.cpp \
+ time.cpp \
+ timecode.cpp \
+ tool.cpp \
+ video.cpp
+
+SRC += \
+ icon.cpp \
+ command.cpp
+
+HEADER = *.h
+
+include ../../Makefile.target
diff --git a/aegisub/src/command/app.cpp b/aegisub/src/command/app.cpp
new file mode 100644
index 000000000..67d804be2
--- /dev/null
+++ b/aegisub/src/command/app.cpp
@@ -0,0 +1,265 @@
+// Copyright (c) 2005-2010, Niels Martin Hansen
+// Copyright (c) 2005-2010, Rodrigo Braz Monteiro
+// Copyright (c) 2010, Amar Takhar
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of the Aegisub Group nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Aegisub Project http://www.aegisub.org/
+//
+// $Id$
+
+/// @file app.cpp
+/// @brief app/ commands.
+/// @ingroup command
+///
+
+#include "config.h"
+
+#ifndef AGI_PRE
+#endif
+
+#include "command.h"
+
+#include "aegisub/context.h"
+#include "main.h"
+
+#include "dialog_about.h"
+#include "audio_controller.h"
+#include "frame_main.h"
+#include "video_context.h"
+#include "utils.h"
+#include "dialog_log.h"
+#include "preferences.h"
+#include "dialog_version_check.h"
+
+
+namespace cmd {
+/// @defgroup cmd-app Application related
+/// @{
+
+/// Launch about dialogue.
+class app_about: public Command {
+public:
+ CMD_NAME("app/about")
+ STR_MENU("&About..")
+ STR_DISP("About")
+ STR_HELP("About Aegisub.")
+
+ void operator()(agi::Context *c) {
+ AboutScreen About(c->parent);
+ About.ShowModal();
+ }
+};
+
+
+/// Display audio and subtitles.
+class app_display_audio_subs: public Command {
+public:
+ CMD_NAME("app/display/audio_subs")
+ STR_MENU("Audio+Subs View")
+ STR_DISP("Audio+Subs View")
+ STR_HELP("Display audio and subtitles only.")
+
+ void operator()(agi::Context *c) {
+ if (!c->audioController->IsAudioOpen()) return;
+ wxGetApp().frame->SetDisplayMode(0,1);
+ }
+};
+
+
+/// Display audio, video and subtitles.
+class app_display_full: public Command {
+public:
+ CMD_NAME("app/display/full")
+ STR_MENU("Full view")
+ STR_DISP("Full view")
+ STR_HELP("Display audio, video and subtitles.")
+
+ void operator()(agi::Context *c) {
+ if (!c->audioController->IsAudioOpen() || !VideoContext::Get()->IsLoaded()) return;
+ wxGetApp().frame->SetDisplayMode(1,1);
+ }
+};
+
+
+/// Display subtitles only.
+class app_display_subs: public Command {
+public:
+ CMD_NAME("app/display/subs")
+ STR_MENU("Subs Only View")
+ STR_DISP("Subs Only View")
+ STR_HELP("Display subtitles only.")
+
+ void operator()(agi::Context *c) {
+ wxGetApp().frame->SetDisplayMode(0,0);
+ }
+};
+
+
+/// Display video and subtitles only.
+class app_display_video_subs: public Command {
+public:
+ CMD_NAME("app/display/video_subs")
+ STR_MENU("Video+Subs View")
+ STR_DISP("Video+Subs View")
+ STR_HELP("Display video and subtitles only.")
+
+ void operator()(agi::Context *c) {
+ wxGetApp().frame->SetDisplayMode(1,0);
+ }
+};
+
+
+/// Exit the application.
+class app_exit: public Command {
+public:
+ CMD_NAME("app/exit")
+ STR_MENU("E&xit")
+ STR_DISP("Exit")
+ STR_HELP("Exit the application.")
+
+ void operator()(agi::Context *c) {
+ printf("XXX: not working yet\n");
+ }
+};
+
+
+/// Select Aegisub interface language
+class app_language: public Command {
+public:
+ CMD_NAME("app/language")
+ STR_MENU("&Language...")
+ STR_DISP("Language")
+ STR_HELP("Select Aegisub interface language")
+
+ void operator()(agi::Context *c) {
+ // Get language
+ AegisubApp *app = (AegisubApp*) wxTheApp;
+ int old = app->locale.curCode;
+ int newCode = app->locale.PickLanguage();
+ // Is OK?
+ if (newCode != -1) {
+ // Set code
+ OPT_SET("App/Locale")->SetInt(newCode);
+
+ // Language actually changed?
+ if (newCode != old) {
+ // Ask to restart program
+ int result = wxMessageBox(_T("Aegisub needs to be restarted so that the new language can be applied. Restart now?"),_T("Restart Aegisub?"),wxICON_QUESTION | wxYES_NO);
+ if (result == wxYES) {
+ // Restart Aegisub
+ if (wxGetApp().frame->Close()) {
+ RestartAegisub();
+ //wxStandardPaths stand;
+ //wxExecute(_T("\"") + stand.GetExecutablePath() + _T("\""));
+ }
+ }
+ }
+ }
+ }
+};
+
+
+/// Event log.
+class app_log: public Command {
+public:
+ CMD_NAME("app/log")
+ STR_MENU("&Log window...")
+ STR_DISP("Log window")
+ STR_HELP("Event log.")
+
+ void operator()(agi::Context *c) {
+ LogWindow *log = new LogWindow(c->parent);
+ log->Show(1);
+ }
+};
+
+
+/// Open a new application window.
+class app_new_window: public Command {
+public:
+ CMD_NAME("app/new_window")
+ STR_MENU("New Window")
+ STR_DISP("New Window")
+ STR_HELP("Open a new application window.")
+
+ void operator()(agi::Context *c) {
+ RestartAegisub();
+ }
+};
+
+
+/// Configure Aegisub.
+class app_options: public Command {
+public:
+ CMD_NAME("app/options")
+ STR_MENU("&Options..")
+ STR_DISP("Options")
+ STR_HELP("Configure Aegisub.")
+
+ void operator()(agi::Context *c) {
+ try {
+ Preferences pref(c->parent);
+ pref.ShowModal();
+ } catch (agi::Exception& e) {
+ LOG_E("config/init") << "Caught exception: " << e.GetName() << " -> " << e.GetMessage();
+ }
+ }
+};
+
+
+/// Check to see if there is a new version of Aegisub available.
+class app_updates: public Command {
+public:
+ CMD_NAME("app/updates")
+ STR_MENU("&Check for Updates..")
+ STR_DISP("Check for Updates")
+ STR_HELP("Check to see if there is a new version of Aegisub available.")
+
+ void operator()(agi::Context *c) {
+ PerformVersionCheck(true);
+ }
+};
+
+/// @}
+
+/// Init app/ commands
+void init_app(CommandManager *cm) {
+ cm->reg(new app_about());
+ cm->reg(new app_display_audio_subs());
+ cm->reg(new app_display_full());
+ cm->reg(new app_display_subs());
+ cm->reg(new app_display_video_subs());
+ cm->reg(new app_exit());
+ cm->reg(new app_language());
+ cm->reg(new app_log());
+ cm->reg(new app_new_window());
+ cm->reg(new app_options());
+ cm->reg(new app_updates());
+}
+
+} // namespace cmd
+
diff --git a/aegisub/src/command/audio.cpp b/aegisub/src/command/audio.cpp
new file mode 100644
index 000000000..06fb685b4
--- /dev/null
+++ b/aegisub/src/command/audio.cpp
@@ -0,0 +1,174 @@
+// Copyright (c) 2005-2010, Niels Martin Hansen
+// Copyright (c) 2005-2010, Rodrigo Braz Monteiro
+// Copyright (c) 2010, Amar Takhar
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of the Aegisub Group nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Aegisub Project http://www.aegisub.org/
+//
+// $Id$
+
+/// @file audio.cpp
+/// @brief audio/ commands.
+/// @ingroup command
+///
+
+#include "config.h"
+
+#ifndef AGI_PRE
+#include
+#endif
+
+#include "command.h"
+
+#include "aegisub/context.h"
+#include "compat.h"
+#include "main.h"
+
+namespace cmd {
+/// @defgroup cmd-audio Audio commands.
+/// @{
+
+
+/// Closes the currently open audio file.
+class audio_close: public Command {
+public:
+ CMD_NAME("audio/close")
+ STR_MENU("&Close Audio")
+ STR_DISP("Close Audio")
+ STR_HELP("Closes the currently open audio file.")
+
+ void operator()(agi::Context *c) {
+ c->audioController->CloseAudio();
+ }
+};
+
+
+/// Opens an audio file.
+class audio_open: public Command {
+public:
+ CMD_NAME("audio/open")
+ STR_MENU("&Open Audio File..")
+ STR_DISP("Open Audio File")
+ STR_HELP("Opens an audio file.")
+
+ void operator()(agi::Context *c) {
+ wxString path = lagi_wxString(OPT_GET("Path/Last/Audio")->GetString());
+ wxString str = wxString(_("Audio Formats")) + _T(" (*.wav,*.mp3,*.ogg,*.flac,*.mp4,*.ac3,*.aac,*.mka,*.m4a,*.w64)|*.wav;*.mp3;*.ogg;*.flac;*.mp4;*.ac3;*.aac;*.mka;*.m4a;*.w64|")
+ + _("Video Formats") + _T(" (*.avi,*.mkv,*.ogm,*.mpg,*.mpeg)|*.avi;*.mkv;*.ogm;*.mp4;*.mpeg;*.mpg|")
+ + _("All files") + _T(" (*.*)|*.*");
+ wxString filename = wxFileSelector(_("Open audio file"),path,_T(""),_T(""),str,wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+ if (!filename.empty()) {
+ c->audioController->OpenAudio(filename);
+ OPT_SET("Path/Last/Audio")->SetString(STD_STR(filename));
+ }
+ }
+};
+
+
+/// Open a 150 minutes blank audio clip, for debugging.
+class audio_open_blank: public Command {
+public:
+ CMD_NAME("audio/open/blank")
+ STR_MENU("Open 2h30 Blank Audio")
+ STR_DISP("Open 2h30 Blank Audio")
+ STR_HELP("Open a 150 minutes blank audio clip, for debugging.")
+
+ void operator()(agi::Context *c) {
+ c->audioController->OpenAudio(_T("dummy-audio:silence?sr=44100&bd=16&ch=1&ln=396900000"));
+ }
+};
+
+
+/// Open a 150 minutes noise-filled audio clip, for debugging.
+class audio_open_noise: public Command {
+public:
+ CMD_NAME("audio/open/noise")
+ STR_MENU("Open 2h30 Noise Audio")
+ STR_DISP("Open 2h30 Noise Audio")
+ STR_HELP("Open a 150 minutes noise-filled audio clip, for debugging.")
+
+ void operator()(agi::Context *c) {
+ c->audioController->OpenAudio(_T("dummy-audio:noise?sr=44100&bd=16&ch=1&ln=396900000"));
+ }
+};
+
+
+/// Opens the audio from the current video file.
+class audio_open_video: public Command {
+public:
+ CMD_NAME("audio/open/video")
+ STR_MENU("Open Audio from &Video")
+ STR_DISP("Open Audio from Video")
+ STR_HELP("Opens the audio from the current video file.")
+
+ void operator()(agi::Context *c) {
+ c->audioController->OpenAudio(_T("audio-video:cache"));
+ }
+};
+
+
+/// Display audio as a frequency-power spectrograph.
+class audio_view_spectrum: public Command {
+public:
+ CMD_NAME("audio/view/spectrum")
+ STR_MENU("Spectrum Display")
+ STR_DISP("Spectrum Display")
+ STR_HELP("Display audio as a frequency-power spectrograph.")
+
+ void operator()(agi::Context *c) {
+ printf("XXX: fixme\n");
+ }
+};
+
+
+/// Display audio as a linear amplitude graph.
+class audio_view_waveform: public Command {
+public:
+ CMD_NAME("audio/view/waveform")
+ STR_MENU("Waveform Display")
+ STR_DISP("Waveform Display")
+ STR_HELP("Display audio as a linear amplitude graph.")
+
+ void operator()(agi::Context *c) {
+ printf("XXX: fixme\n");
+ }
+};
+
+/// @}
+
+/// Init audio/ commands
+void init_audio(CommandManager *cm) {
+ cm->reg(new audio_close());
+ cm->reg(new audio_open());
+ cm->reg(new audio_open_blank());
+ cm->reg(new audio_open_noise());
+ cm->reg(new audio_open_video());
+ cm->reg(new audio_view_spectrum());
+ cm->reg(new audio_view_waveform());
+}
+
+} // namespace cmd
diff --git a/aegisub/src/command/automation.cpp b/aegisub/src/command/automation.cpp
new file mode 100644
index 000000000..2dd968ef0
--- /dev/null
+++ b/aegisub/src/command/automation.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2005-2010, Niels Martin Hansen
+// Copyright (c) 2005-2010, Rodrigo Braz Monteiro
+// Copyright (c) 2010, Amar Takhar
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of the Aegisub Group nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Aegisub Project http://www.aegisub.org/
+//
+// $Id$
+
+/// @file automation.cpp
+/// @brief am/ (automation) commands
+/// @ingroup command
+///
+
+#include "config.h"
+
+#ifndef AGI_PRE
+#endif
+
+#include "command.h"
+
+#include "main.h"
+#include "aegisub/context.h"
+#include "dialog_automation.h"
+#include "auto4_base.h"
+#include "video_context.h"
+#include "frame_main.h"
+
+namespace cmd {
+/// @defgroup cmd-am Automation commands
+/// @{
+
+
+/// Open automation manager.
+class am_manager: public Command {
+public:
+ CMD_NAME("am/manager")
+ STR_MENU("&Automation..")
+ STR_DISP("Automation")
+ STR_HELP("Open automation manager.")
+
+ void operator()(agi::Context *c) {
+#ifdef WITH_AUTOMATION
+#ifdef __APPLE__
+ if (wxGetMouseState().CmdDown()) {
+#else
+ if (wxGetMouseState().ControlDown()) {
+#endif
+ wxGetApp().global_scripts->Reload();
+ if (wxGetMouseState().ShiftDown()) {
+ const std::vector scripts = c->local_scripts->GetScripts();
+ for (size_t i = 0; i < scripts.size(); ++i) {
+ try {
+ scripts[i]->Reload();
+ } catch (const wchar_t *e) {
+ wxLogError(e);
+ } catch (...) {
+ wxLogError(_T("An unknown error occurred reloading Automation script '%s'."), scripts[i]->GetName().c_str());
+ }
+ }
+
+ wxGetApp().frame->StatusTimeout(_("Reloaded all Automation scripts"));
+ } else {
+ wxGetApp().frame->StatusTimeout(_("Reloaded autoload Automation scripts"));
+ }
+ } else {
+ VideoContext::Get()->Stop();
+ DialogAutomation dlg(c->parent, c->local_scripts);
+ dlg.ShowModal();
+ }
+#endif
+ }
+};
+
+/// @}
+
+/// Init am/ commands. (automation)
+void init_automation(CommandManager *cm) {
+ cm->reg(new am_manager());
+}
+
+
+} // namespace cmd
diff --git a/aegisub/src/command/command.cpp b/aegisub/src/command/command.cpp
new file mode 100644
index 000000000..c7de64dc1
--- /dev/null
+++ b/aegisub/src/command/command.cpp
@@ -0,0 +1,129 @@
+// Copyright (c) 2010, Amar Takhar
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// $Id$
+
+/// @file command.cpp
+/// @brief Command system base file.
+/// @ingroup command
+
+#include "command.h"
+#include
+
+namespace cmd {
+
+CommandManager *cm;
+
+int id(std::string name) { return cm->id(name); }
+void call(agi::Context *c, const int id) { return cm->call(c, id); }
+int count() { return cm->count(); }
+Command* get(std::string name) { return cm->get(name); }
+
+
+wxBitmap* Command::Icon(int size) {
+ if (size == 16) {
+ return icon::get(name(), 16);
+ } else if (size == 24) {
+ return icon::get(name(), 24);
+ } else {
+ throw CommandIconInvalid("Valid icon sizes are 16 or 24.");
+ }
+}
+
+
+int CommandManager::id(std::string name) {
+
+ cmdMap::iterator index;
+
+ if ((index = map.find(name)) != map.end()) {
+ int id = std::distance(map.begin(), index);
+ return id;
+ }
+ // XXX: throw
+ printf("cmd::id NOT FOUND (%s)\n", name.c_str());
+ return 60003;
+}
+
+
+Command* CommandManager::get(std::string name) {
+ cmdMap::iterator index;
+
+ if ((index = map.find(name)) != map.end()) {
+ return index->second;
+ }
+ // XXX: throw
+ printf("cmd::id NOT FOUND (%s)\n", name.c_str());
+}
+
+
+
+
+void CommandManager::call(agi::Context *c, const int id) {
+ cmdMap::iterator index(map.begin());
+ std::advance(index, id);
+
+ if (index != map.end()) {
+ LOG_D("event/command") << index->first << " " << "(Id: " << id << ")";
+ (*index->second)(c);
+ } else {
+ LOG_W("event/command/not_found") << "EVENT ID NOT FOUND: " << id;
+ // XXX: throw
+ }
+}
+
+
+void CommandManager::reg(Command *cmd) {
+ map.insert(cmdPair(cmd->name(), cmd));
+}
+
+
+// These forward declarations exist here since we don't want to expose
+// them in a header, they're strictly internal-use.
+void init_app(CommandManager *cm);
+void init_audio(CommandManager *cm);
+void init_automation(CommandManager *cm);
+void init_command(CommandManager *cm);
+void init_edit(CommandManager *cm);
+void init_grid(CommandManager *cm);
+void init_help(CommandManager *cm);
+void init_keyframe(CommandManager *cm);
+void init_medusa(CommandManager *cm);
+void init_menu(CommandManager *cm);
+void init_recent(CommandManager *cm);
+void init_subtitle(CommandManager *cm);
+void init_time(CommandManager *cm);
+void init_timecode(CommandManager *cm);
+void init_tool(CommandManager *cm);
+void init_video(CommandManager *cm);
+
+void init_command(CommandManager *cm) {
+ LOG_D("command/init") << "Populating command map";
+ init_app(cm);
+ init_audio(cm);
+ init_automation(cm);
+ init_edit(cm);
+ init_grid(cm);
+ init_help(cm);
+ init_keyframe(cm);
+ init_medusa(cm);
+ init_menu(cm);
+ init_recent(cm);
+ init_subtitle(cm);
+ init_time(cm);
+ init_timecode(cm);
+ init_tool(cm);
+ init_video(cm);
+}
+
+} // namespace cmd
diff --git a/aegisub/src/command/command.h b/aegisub/src/command/command.h
new file mode 100644
index 000000000..993b5049e
--- /dev/null
+++ b/aegisub/src/command/command.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2010, Amar Takhar
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// $Id$
+
+/// @file command.h
+/// @brief Command base class and main header.
+/// @ingroup command
+
+#include "aegisub/context.h"
+#include "icon.h"
+
+DEFINE_BASE_EXCEPTION_NOINNER(CommandError, agi::Exception)
+DEFINE_SIMPLE_EXCEPTION_NOINNER(CommandIconNone, CommandError, "command/icon")
+DEFINE_SIMPLE_EXCEPTION_NOINNER(CommandIconInvalid, CommandError, "command/icon/invalid")
+
+#define CMD_NAME(a) const char* name() { return a; }
+#define STR_MENU(a) wxString StrMenu() const { return a; }
+#define STR_DISP(a) wxString StrDisplay() const { return a; }
+#define STR_HELP(a) wxString StrHelp() const { return a; }
+
+/// Commands
+namespace cmd {
+ class CommandManager;
+ class Command;
+
+ /// CommandManager instance.
+ extern CommandManager *cm;
+
+ /// Init all commands.
+ /// @param cm CommandManager instance.
+ void init_command(CommandManager *cm);
+
+ // The following are nothing more than glorified macros.
+ int id(std::string name); ///< @see CommandManager::id
+ void call(agi::Context *c, const int id); ///< @see CommandManager::call
+ int count(); ///< @see CommandManager::count
+ Command* get(std::string name); ///< @see CommandManager::get
+
+
+ /// Holds an individual Command
+ class Command {
+ public:
+ virtual const char* name()=0; ///< Command name.
+ virtual wxString StrMenu() const=0; ///< String for menu purposes including accelerators.
+ virtual wxString StrDisplay() const=0; ///< Plain string for display purposes.
+ virtual wxString StrHelp() const=0; ///< Short help string descripting the command purpose.
+
+ /// Request icon.
+ /// @param size Icon size.
+ wxBitmap* Icon(int size);
+
+ /// Command function
+ virtual void operator()(agi::Context *c)=0;
+
+ /// Destructor
+ virtual ~Command() {};
+ };
+
+
+ /// Manager for commands
+ class CommandManager {
+ typedef std::map cmdMap; ///< Map to hold commands.
+ typedef std::pair cmdPair; ///< Pair for command insertion.
+ cmdMap map; ///< Actual map.
+
+ public:
+ /// Register a command.
+ /// @param cmd Command object.
+ void reg(Command *cmd);
+
+ /// Retrieve an ID for event usage or otherwise
+ /// @param name Command name
+ /// @return Command ID
+ /// @note This is guaranteed to be unique.
+ int id(std::string name);
+
+ /// Call a command.
+ /// @param c Current Context.
+ /// @param id ID for Command to call.
+ void call(agi::Context *c, const int id);
+
+ /// Count number of commands.
+ /// @return ID number.
+ int count() { return map.size(); }
+
+ /// Retrieve a Command object.
+ /// @param Command object.
+ Command* get(std::string name);
+ };
+} // namespace cmd
diff --git a/aegisub/src/command/edit.cpp b/aegisub/src/command/edit.cpp
new file mode 100644
index 000000000..9e20b9b28
--- /dev/null
+++ b/aegisub/src/command/edit.cpp
@@ -0,0 +1,318 @@
+// Copyright (c) 2005-2010, Niels Martin Hansen
+// Copyright (c) 2005-2010, Rodrigo Braz Monteiro
+// Copyright (c) 2010, Amar Takhar
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of the Aegisub Group nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Aegisub Project http://www.aegisub.org/
+//
+// $Id$
+
+/// @file edit.cpp
+/// @brief edit/ commands.
+/// @ingroup command
+///
+
+#include "config.h"
+
+#ifndef AGI_PRE
+#endif
+
+#include "command.h"
+
+#include "aegisub/context.h"
+#include "aegisub/context.h"
+#include "subs_edit_box.h"
+#include "subs_edit_ctrl.h"
+#include "dialog_search_replace.h"
+#include "video_context.h"
+
+namespace cmd {
+/// @defgroup cmd-edit Editing commands.
+/// @{
+
+
+/// Copy subtitles.
+class edit_line_copy: public Command {
+public:
+ CMD_NAME("edit/line/copy")
+ STR_MENU("Copy Lines")
+ STR_DISP("Copy Lines")
+ STR_HELP("Copy subtitles.")
+
+ void operator()(agi::Context *c) {
+ if (c->parent->FindFocus() == c->EditBox->TextEdit) {
+ c->EditBox->TextEdit->Copy();
+ return;
+ }
+ c->SubsGrid->CopyLines(c->SubsGrid->GetSelection());
+ }
+};
+
+
+/// Cut subtitles.
+class edit_line_cut: public Command {
+public:
+ CMD_NAME("edit/line/cut")
+ STR_MENU("Cut Lines")
+ STR_DISP("Cut Lines")
+ STR_HELP("Cut subtitles.")
+
+ void operator()(agi::Context *c) {
+ if (c->parent->FindFocus() == c->EditBox->TextEdit) {
+ c->EditBox->TextEdit->Cut();
+ return;
+ }
+ c->SubsGrid->CutLines(c->SubsGrid->GetSelection());
+ }
+};
+
+
+/// Delete currently selected lines.
+class edit_line_delete: public Command {
+public:
+ CMD_NAME("edit/line/delete")
+ STR_MENU("Delete Lines")
+ STR_DISP("Delete Lines")
+ STR_HELP("Delete currently selected lines.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Duplicate the selected lines.
+class edit_line_duplicate: public Command {
+public:
+ CMD_NAME("edit/line/duplicate")
+ STR_MENU("&Duplicate Lines")
+ STR_DISP("Duplicate Lines")
+ STR_HELP("Duplicate the selected lines.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Duplicate lines and shift by one frame.
+class edit_line_duplicate_shift: public Command {
+public:
+ CMD_NAME("edit/line/duplicate/shift")
+ STR_MENU("&Duplicate and Shift by 1 Frame")
+ STR_DISP("Duplicate and Shift by 1 Frame")
+ STR_HELP("Duplicate lines and shift by one frame.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Joins selected lines in a single one, as karaoke.
+class edit_line_join_as_karaoke: public Command {
+public:
+ CMD_NAME("edit/line/join/as_karaoke")
+ STR_MENU("As &Karaoke")
+ STR_DISP("As Karaoke")
+ STR_HELP("Joins selected lines in a single one, as karaoke.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Joins selected lines in a single one, concatenating text together.
+class edit_line_join_concatenate: public Command {
+public:
+ CMD_NAME("edit/line/join/concatenate")
+ STR_MENU("&Concatenate")
+ STR_DISP("Concatenate")
+ STR_HELP("Joins selected lines in a single one, concatenating text together.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Joins selected lines in a single one, keeping text of first and discarding remaining.
+class edit_line_join_keep_first: public Command {
+public:
+ CMD_NAME("edit/line/join/keep_first")
+ STR_MENU("Keep &First")
+ STR_DISP("Keep First")
+ STR_HELP("Joins selected lines in a single one, keeping text of first and discarding remaining.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Paste subtitles.
+class edit_line_paste: public Command {
+public:
+ CMD_NAME("edit/line/paste")
+ STR_MENU("Paste Lines")
+ STR_DISP("Paste Lines")
+ STR_HELP("Paste subtitles.")
+
+ void operator()(agi::Context *c) {
+ if (c->parent->FindFocus() == c->EditBox->TextEdit) {
+ c->EditBox->TextEdit->Paste();
+ return;
+ }
+ c->SubsGrid->PasteLines(c->SubsGrid->GetFirstSelRow());
+ }
+};
+
+
+/// Paste subtitles over others.
+class edit_line_paste_over: public Command {
+public:
+ CMD_NAME("edit/line/paste/over")
+ STR_MENU("Paste Lines Over..")
+ STR_DISP("Paste Lines Over")
+ STR_HELP("Paste subtitles over others.")
+
+ void operator()(agi::Context *c) {
+ c->SubsGrid->PasteLines(c->SubsGrid->GetFirstSelRow(),true);
+ }
+};
+
+
+/// Recombine subtitles when they have been split and merged.
+class edit_line_recombine: public Command {
+public:
+ CMD_NAME("edit/line/recombine")
+ STR_MENU("Recombine Lines")
+ STR_DISP("Recombine Lines")
+ STR_HELP("Recombine subtitles when they have been split and merged.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Uses karaoke timing to split line into multiple smaller lines.
+class edit_line_split_by_karaoke: public Command {
+public:
+ CMD_NAME("edit/line/split/by_karaoke")
+ STR_MENU("Split Lines (by karaoke)")
+ STR_DISP("Split Lines (by karaoke)")
+ STR_HELP("Uses karaoke timing to split line into multiple smaller lines.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Swaps the two selected lines.
+class edit_line_swap: public Command {
+public:
+ CMD_NAME("edit/line/swap")
+ STR_MENU("Swap Lines")
+ STR_DISP("Swap Lines")
+ STR_HELP("Swaps the two selected lines.")
+
+ void operator()(agi::Context *c) {
+//XXX: subs_grid.cpp
+ }
+};
+
+
+/// Redoes last action.
+class edit_redo: public Command {
+public:
+ CMD_NAME("edit/redo")
+ STR_MENU("&Redo")
+ STR_DISP("Redo")
+ STR_HELP("Redoes last action.")
+
+ void operator()(agi::Context *c) {
+ VideoContext::Get()->Stop();
+ c->ass->Redo();
+ }
+};
+
+
+/// Find and replace words in subtitles.
+class edit_search_replace: public Command {
+public:
+ CMD_NAME("edit/search_replace")
+ STR_MENU("Search and &Replace..")
+ STR_DISP("Search and Replace")
+ STR_HELP("Find and replace words in subtitles.")
+
+ void operator()(agi::Context *c) {
+ VideoContext::Get()->Stop();
+ Search.OpenDialog(true);
+ }
+};
+
+
+/// Undoes last action.
+class edit_undo: public Command {
+public:
+ CMD_NAME("edit/undo")
+ STR_MENU("&Undo")
+ STR_DISP("Undo")
+ STR_HELP("Undoes last action.")
+
+ void operator()(agi::Context *c) {
+ VideoContext::Get()->Stop();
+ c->ass->Undo();
+ }
+};
+
+/// @}
+
+/// Init edit/ commands
+void init_edit(CommandManager *cm) {
+ cm->reg(new edit_line_copy());
+ cm->reg(new edit_line_cut());
+ cm->reg(new edit_line_delete());
+ cm->reg(new edit_line_duplicate());
+ cm->reg(new edit_line_duplicate_shift());
+ cm->reg(new edit_line_join_as_karaoke());
+ cm->reg(new edit_line_join_concatenate());
+ cm->reg(new edit_line_join_keep_first());
+ cm->reg(new edit_line_paste());
+ cm->reg(new edit_line_paste_over());
+ cm->reg(new edit_line_recombine());
+ cm->reg(new edit_line_split_by_karaoke());
+ cm->reg(new edit_line_swap());
+ cm->reg(new edit_redo());
+ cm->reg(new edit_search_replace());
+ cm->reg(new edit_undo());
+}
+
+} // namespace cmd
diff --git a/aegisub/src/command/grid.cpp b/aegisub/src/command/grid.cpp
new file mode 100644
index 000000000..b3427f361
--- /dev/null
+++ b/aegisub/src/command/grid.cpp
@@ -0,0 +1,171 @@
+// Copyright (c) 2005-2010, Niels Martin Hansen
+// Copyright (c) 2005-2010, Rodrigo Braz Monteiro
+// Copyright (c) 2010, Amar Takhar
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of the Aegisub Group nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Aegisub Project http://www.aegisub.org/
+//
+// $Id$
+
+/// @file grid.cpp
+/// @brief grid/ commands.
+/// @ingroup command
+///
+
+#include "config.h"
+
+#ifndef AGI_PRE
+#endif
+
+#include "command.h"
+
+#include "aegisub/context.h"
+#include "subs_grid.h"
+#include "main.h"
+#include "frame_main.h"
+
+namespace cmd {
+/// @defgroup cmd-grid Subtitle grid commands.
+/// @{
+
+
+
+/// Move to the next subtitle line.
+class grid_line_next: public Command {
+public:
+ CMD_NAME("grid/line/next")
+ STR_MENU("Next Line")
+ STR_DISP("Next Line")
+ STR_HELP("Move to the next subtitle line.")
+
+ void operator()(agi::Context *c) {
+ c->SubsGrid->NextLine();
+ }
+};
+
+
+/// Move to the previous line.
+class grid_line_prev: public Command {
+public:
+ CMD_NAME("grid/line/prev")
+ STR_MENU("Previous Line")
+ STR_DISP("Previous Line")
+ STR_HELP("Move to the previous line.")
+
+ void operator()(agi::Context *c) {
+ c->SubsGrid->PrevLine();
+ }
+};
+
+
+/// Cycle through tag hiding modes.
+class grid_tag_cycle_hiding: public Command {
+public:
+ CMD_NAME("grid/tag/cycle_hiding")
+ STR_MENU("Cycle Tag Hiding")
+ STR_DISP("Cycle Tag Hiding")
+ STR_HELP("Cycle through tag hiding modes.")
+
+ void operator()(agi::Context *c) {
+ int tagMode = OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt();
+
+ // Cycle to next
+ tagMode = (tagMode+1)%3;
+
+ // Show on status bar
+ wxString message = _("ASS Override Tag mode set to ");
+ if (tagMode == 0) message += _("show full tags.");
+ if (tagMode == 1) message += _("simplify tags.");
+ if (tagMode == 2) message += _("hide tags.");
+ wxGetApp().frame->StatusTimeout(message,10000);
+
+ // Set option
+ OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(tagMode);
+
+ // Refresh grid
+ c->SubsGrid->Refresh(false);
+ }
+};
+
+
+/// Hide override tags in the subtitle grid.
+class grid_tags_hide: public Command {
+public:
+ CMD_NAME("grid/tags/hide")
+ STR_MENU("Hide Tags")
+ STR_DISP("Hide Tags")
+ STR_HELP("Hide override tags in the subtitle grid.")
+
+ void operator()(agi::Context *c) {
+// XXX: Needs fixing.
+// OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(event.GetId() - cmd::id("subtitle/tags/show"));
+// SubsGrid->Refresh(false);
+ }
+};
+
+
+/// Show full override tags in the subtitle grid.
+class grid_tags_show: public Command {
+public:
+ CMD_NAME("grid/tags/show")
+ STR_MENU("Show Tags")
+ STR_DISP("Show Tags")
+ STR_HELP("Show full override tags in the subtitle grid.")
+
+ void operator()(agi::Context *c) {
+ //XXX: see grid_tags_hide
+ }
+};
+
+
+/// Replace override tags in the subtitle grid with a simplified placeholder.
+class grid_tags_simplify: public Command {
+public:
+ CMD_NAME("grid/tags/simplify")
+ STR_MENU("Simplify Tags")
+ STR_DISP("Simplify Tags")
+ STR_HELP("Replace override tags in the subtitle grid with a simplified placeholder.")
+
+ void operator()(agi::Context *c) {
+ //XXX: see grid_tags_hide
+ }
+};
+
+/// @}
+
+
+/// Init grid/ commands.
+void init_grid(CommandManager *cm) {
+ cm->reg(new grid_line_next());
+ cm->reg(new grid_line_prev());
+ cm->reg(new grid_tag_cycle_hiding());
+ cm->reg(new grid_tags_hide());
+ cm->reg(new grid_tags_show());
+ cm->reg(new grid_tags_simplify());
+}
+
+} // namespace cmd
diff --git a/aegisub/src/hotkeys.h b/aegisub/src/command/header
similarity index 50%
rename from aegisub/src/hotkeys.h
rename to aegisub/src/command/header
index 9668ca25e..cc146d1ac 100644
--- a/aegisub/src/hotkeys.h
+++ b/aegisub/src/command/header
@@ -1,4 +1,4 @@
-// Copyright (c) 2005, Rodrigo Braz Monteiro
+// Copyright (c) 2010, Amar Takhar
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -29,107 +29,11 @@
//
// $Id$
-/// @file hotkeys.h
-/// @see hotkeys.cpp
-/// @ingroup main_ui
+/// @file
+/// @brief
+/// @ingroup command
///
+namespace cmd {
-
-///////////
-// Headers
-#ifndef AGI_PRE
-#include