2007-01-13 03:42:19 +01:00
|
|
|
// Copyright (c) 2005, Dan Donovan (Dansolo)
|
2009-06-08 04:37:09 +02:00
|
|
|
// Copyright (c) 2009, Niels Martin Hansen
|
2007-01-13 03:22:28 +01:00
|
|
|
// 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.
|
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
|
2009-08-04 03:53:26 +02:00
|
|
|
/// @file dialog_kara_timing_copy.cpp
|
2009-08-04 04:00:30 +02:00
|
|
|
/// @brief Karaoke timing copier dialogue box and logic
|
|
|
|
/// @ingroup tools_ui kara_timing_copy
|
2009-07-29 07:43:02 +02:00
|
|
|
///
|
2007-01-15 00:34:27 +01:00
|
|
|
|
2009-01-04 07:31:48 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
#include "dialog_kara_timing_copy.h"
|
|
|
|
|
2009-09-10 15:06:40 +02:00
|
|
|
#ifndef AGI_PRE
|
2009-06-08 04:37:09 +02:00
|
|
|
#include <deque>
|
2009-09-10 15:06:40 +02:00
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
#include <wx/checkbox.h>
|
|
|
|
#include <wx/combobox.h>
|
2011-07-16 08:42:55 +02:00
|
|
|
#include <wx/dcclient.h>
|
2011-09-28 21:44:07 +02:00
|
|
|
#include <wx/listctrl.h>
|
2011-07-16 08:42:55 +02:00
|
|
|
#include <wx/msgdlg.h>
|
2011-09-28 21:44:07 +02:00
|
|
|
#include <wx/regex.h>
|
2011-07-16 08:42:55 +02:00
|
|
|
#include <wx/settings.h>
|
|
|
|
#include <wx/sizer.h>
|
|
|
|
#include <wx/stattext.h>
|
2009-09-10 15:06:40 +02:00
|
|
|
#include <wx/string.h>
|
|
|
|
#endif
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
#include "ass_dialogue.h"
|
2007-01-13 03:22:28 +01:00
|
|
|
#include "ass_file.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "ass_karaoke.h"
|
|
|
|
#include "help_button.h"
|
2011-01-16 08:17:36 +01:00
|
|
|
#include "include/aegisub/context.h"
|
2011-09-28 21:44:07 +02:00
|
|
|
#include "kana_table.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "libresrc/libresrc.h"
|
2010-05-21 03:13:36 +02:00
|
|
|
#include "main.h"
|
2010-12-08 04:36:10 +01:00
|
|
|
#include "selection_controller.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "utils.h"
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
#define TEXT_LABEL_SOURCE _("Source: ")
|
|
|
|
#define TEXT_LABEL_DEST _("Dest: ")
|
|
|
|
|
2011-01-16 08:17:36 +01:00
|
|
|
// IDs
|
|
|
|
enum {
|
|
|
|
BUTTON_KTSTART = 2500,
|
|
|
|
BUTTON_KTLINK,
|
|
|
|
BUTTON_KTUNLINK,
|
|
|
|
BUTTON_KTSKIPSOURCE,
|
|
|
|
BUTTON_KTSKIPDEST,
|
|
|
|
BUTTON_KTGOBACK,
|
2012-03-27 02:48:38 +02:00
|
|
|
BUTTON_KTACCEPT
|
2011-01-16 08:17:36 +01:00
|
|
|
};
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
|
|
|
/// DOCME
|
|
|
|
/// @class KaraokeLineMatchDisplay
|
|
|
|
/// @brief DOCME
|
|
|
|
///
|
|
|
|
/// DOCME
|
2009-06-08 04:37:09 +02:00
|
|
|
class KaraokeLineMatchDisplay : public wxControl {
|
2011-09-28 21:44:07 +02:00
|
|
|
typedef AssKaraoke::Syllable MatchSyllable;
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
struct MatchGroup {
|
2009-06-25 04:31:35 +02:00
|
|
|
std::vector<MatchSyllable> src;
|
2009-06-08 04:37:09 +02:00
|
|
|
wxString dst;
|
|
|
|
int last_render_width;
|
2011-09-28 21:44:07 +02:00
|
|
|
MatchGroup() : last_render_width(0) { }
|
2009-06-08 04:37:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<MatchGroup> matched_groups;
|
2009-06-25 04:31:35 +02:00
|
|
|
std::deque<MatchSyllable> unmatched_source;
|
2009-06-08 04:37:09 +02:00
|
|
|
wxString unmatched_destination;
|
|
|
|
|
|
|
|
int last_total_matchgroup_render_width;
|
|
|
|
|
2010-06-11 04:24:59 +02:00
|
|
|
size_t source_sel_length;
|
|
|
|
size_t destination_sel_length;
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
void OnPaint(wxPaintEvent &event);
|
|
|
|
|
|
|
|
const wxString label_source, label_destination;
|
|
|
|
|
|
|
|
public:
|
2011-09-28 21:44:07 +02:00
|
|
|
/// Start processing a new line pair
|
|
|
|
void SetInputData(AssDialogue *src, AssDialogue *dst);
|
|
|
|
/// Build and return the output line from the matched syllables
|
|
|
|
wxString GetOutputLine() const;
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
/// Number of syllables not yet matched from source
|
|
|
|
size_t GetRemainingSource() const { return unmatched_source.size(); }
|
|
|
|
/// Number of characters not yet matched from destination
|
|
|
|
size_t GetRemainingDestination() const { return unmatched_destination.size(); }
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
// Adjust source and destination match lengths
|
|
|
|
void IncreaseSourceMatch();
|
|
|
|
void DecreaseSourceMatch();
|
|
|
|
void IncreseDestinationMatch();
|
|
|
|
void DecreaseDestinationMatch();
|
2011-09-28 21:44:07 +02:00
|
|
|
/// Attempt to treat source as Japanese romaji, destination as Japanese kana+kanji, and make an automatic match
|
2009-06-08 04:37:09 +02:00
|
|
|
void AutoMatchJapanese();
|
2011-09-28 21:44:07 +02:00
|
|
|
/// Accept current selection and save match
|
2009-06-08 04:37:09 +02:00
|
|
|
bool AcceptMatch();
|
2011-09-28 21:44:07 +02:00
|
|
|
/// Undo last match, adding it back to the unmatched input
|
2009-06-08 04:37:09 +02:00
|
|
|
bool UndoMatch();
|
|
|
|
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
KaraokeLineMatchDisplay(wxWindow *parent);
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
wxSize GetBestSize() const;
|
2009-06-08 04:37:09 +02:00
|
|
|
};
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
KaraokeLineMatchDisplay::KaraokeLineMatchDisplay(wxWindow *parent)
|
2009-06-08 04:37:09 +02:00
|
|
|
: wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE|wxWANTS_CHARS)
|
|
|
|
, label_source(TEXT_LABEL_SOURCE)
|
|
|
|
, label_destination(TEXT_LABEL_DEST)
|
|
|
|
{
|
|
|
|
InheritAttributes();
|
|
|
|
SetInputData(0, 0);
|
|
|
|
|
|
|
|
wxSize best_size = GetBestSize();
|
|
|
|
SetMaxSize(wxSize(-1, best_size.GetHeight()));
|
|
|
|
SetMinSize(best_size);
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2012-09-25 01:35:27 +02:00
|
|
|
Bind(wxEVT_SET_FOCUS, std::bind(&wxControl::Refresh, this, true, (const wxRect*)0));
|
|
|
|
Bind(wxEVT_KILL_FOCUS, std::bind(&wxControl::Refresh, this, true, (const wxRect*)0));
|
2011-09-28 21:44:07 +02:00
|
|
|
Bind(wxEVT_PAINT, &KaraokeLineMatchDisplay::OnPaint, this);
|
2009-06-08 04:37:09 +02:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
wxSize KaraokeLineMatchDisplay::GetBestSize() const
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
int w_src, h_src, w_dst, h_dst;
|
|
|
|
GetTextExtent(label_source, &w_src, &h_src);
|
|
|
|
GetTextExtent(label_destination, &w_dst, &h_dst);
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
int min_width = std::max(w_dst, w_src);
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
// Magic number 7:
|
|
|
|
// 1 pixel for top black border, 1 pixel white top border in first row, 1 pixel white bottom padding in first row
|
|
|
|
// 1 pixel middle border, 1 pixel top and 1 pixel bottom padding in second row, 1 pixel bottom border
|
|
|
|
return wxSize(min_width * 2, h_src + h_dst + 7);
|
|
|
|
}
|
|
|
|
|
|
|
|
int DrawBoxedText(wxDC &dc, const wxString &txt, int x, int y)
|
|
|
|
{
|
|
|
|
int tw, th;
|
|
|
|
// Assume the pen, brush and font properties have already been set in the DC.
|
|
|
|
// Return the advance width, including box margins, borders etc
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
if (txt.empty())
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
// Empty string gets special handling:
|
|
|
|
// The box is drawn in shorter width, to emphasize it's empty
|
|
|
|
// GetTextExtent has to be called with a non-empty string, otherwise it returns the wrong height
|
2011-09-28 21:43:11 +02:00
|
|
|
dc.GetTextExtent(" ", &tw, &th);
|
2009-06-08 04:37:09 +02:00
|
|
|
dc.DrawRectangle(x, y-2, 4, th+4);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dc.GetTextExtent(txt, &tw, &th);
|
|
|
|
dc.DrawRectangle(x, y-2, tw+4, th+4);
|
|
|
|
dc.DrawText(txt, x+2, y);
|
|
|
|
return tw+3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void KaraokeLineMatchDisplay::OnPaint(wxPaintEvent &)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
wxPaintDC dc(this);
|
|
|
|
|
|
|
|
wxColour outer_text(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
|
|
|
wxColour outer_back(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
2009-06-08 05:34:09 +02:00
|
|
|
wxColour outer_frame(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
2009-06-08 04:37:09 +02:00
|
|
|
wxColour inner_back(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
|
|
|
wxColour inner_text(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
|
|
|
wxColour sel_back(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
|
|
|
|
wxColour sel_text(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
|
|
|
|
|
|
|
// Y coordinates of the top and bottom lines
|
|
|
|
int y_line1, y_line2, y_line3;
|
|
|
|
// Next X coordinate to draw a matched syllable at
|
|
|
|
int next_x;
|
|
|
|
|
|
|
|
wxSize client_size = GetClientSize();
|
|
|
|
|
|
|
|
// Calculate the text line positions
|
|
|
|
{
|
|
|
|
int x, y, x2;
|
|
|
|
GetTextExtent(label_source, &x, &y);
|
|
|
|
y_line1 = 2;
|
|
|
|
y_line2 = y_line1 + y + 3;
|
|
|
|
y_line3 = y_line2 + y + 3;
|
|
|
|
GetTextExtent(label_destination, &x2, &y);
|
|
|
|
next_x = (x2 > x) ? x2 : x;
|
|
|
|
next_x += 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Paint the labels
|
|
|
|
dc.SetTextBackground(outer_back);
|
|
|
|
if (FindFocus() == this)
|
|
|
|
dc.SetTextForeground(outer_text);
|
|
|
|
else
|
|
|
|
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
|
|
|
|
dc.SetFont(GetFont());
|
|
|
|
dc.SetBackgroundMode(wxTRANSPARENT);
|
|
|
|
dc.DrawText(label_source, wxPoint(0, y_line1));
|
|
|
|
dc.DrawText(label_destination, wxPoint(0, y_line2));
|
|
|
|
|
|
|
|
// Horizontal lines through the width of the control
|
|
|
|
dc.SetPen(wxPen(outer_frame));
|
|
|
|
dc.DrawLine(next_x-1, y_line1-2, client_size.GetWidth(), y_line1-2);
|
|
|
|
dc.DrawLine(next_x-1, y_line2-2, client_size.GetWidth(), y_line2-2);
|
|
|
|
dc.DrawLine(next_x-1, y_line3-2, client_size.GetWidth(), y_line3-2);
|
|
|
|
|
|
|
|
// Draw matched groups
|
|
|
|
int this_total_matchgroup_render_width = 0;
|
|
|
|
bool scroll_arrows_drawn = false;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto& grp : matched_groups)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
int prev_x = next_x;
|
|
|
|
|
|
|
|
// Skip groups that would cause the input part to be too far right
|
|
|
|
this_total_matchgroup_render_width += grp.last_render_width;
|
|
|
|
if (last_total_matchgroup_render_width - this_total_matchgroup_render_width > client_size.x / 2)
|
|
|
|
{
|
|
|
|
// If we're skipping some syllables, show an arrow as feedback that something is scrolled off
|
|
|
|
if (!scroll_arrows_drawn)
|
|
|
|
{
|
|
|
|
dc.SetBrush(wxBrush(outer_frame));
|
|
|
|
wxPoint triangle[3];
|
|
|
|
int height = y_line2 - y_line1;
|
|
|
|
triangle[0] = wxPoint(next_x-3, height/2);
|
|
|
|
triangle[1] = wxPoint(next_x-3+height/2, 0);
|
|
|
|
triangle[2] = wxPoint(next_x-3+height/2, height);
|
|
|
|
dc.DrawPolygon(3, triangle, 0, 0);
|
|
|
|
dc.DrawPolygon(3, triangle, 0, height);
|
|
|
|
next_x += height/2 - 4;
|
|
|
|
}
|
|
|
|
scroll_arrows_drawn = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dc.SetPen(wxPen(outer_frame));
|
|
|
|
dc.SetBrush(wxBrush(inner_back));
|
|
|
|
dc.SetTextBackground(inner_back);
|
|
|
|
dc.SetTextForeground(inner_text);
|
|
|
|
|
|
|
|
// Matched source syllables
|
|
|
|
int syl_x = next_x;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto const& syl : grp.src)
|
|
|
|
syl_x += DrawBoxedText(dc, syl.text, syl_x, y_line1);
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
// Matched destination text
|
|
|
|
{
|
|
|
|
int adv = DrawBoxedText(dc, grp.dst, next_x, y_line2);
|
2012-03-25 06:05:06 +02:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
// Adjust next_x here while we have the text_w
|
|
|
|
if (syl_x > next_x + adv)
|
|
|
|
next_x = syl_x;
|
|
|
|
else
|
|
|
|
next_x = next_x + adv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Spacing between groups
|
|
|
|
next_x += 3;
|
|
|
|
grp.last_render_width = next_x - prev_x;
|
|
|
|
}
|
|
|
|
|
|
|
|
last_total_matchgroup_render_width = this_total_matchgroup_render_width;
|
|
|
|
|
|
|
|
// Spacing between grouped and ungrouped parts
|
|
|
|
next_x += 4;
|
|
|
|
|
|
|
|
// Remaining source syllables
|
|
|
|
int syl_x = next_x;
|
|
|
|
// Start out with highlight colours
|
|
|
|
dc.SetTextBackground(sel_back);
|
|
|
|
dc.SetTextForeground(sel_text);
|
|
|
|
dc.SetBrush(wxBrush(sel_back));
|
|
|
|
for (size_t j = 0; j < unmatched_source.size(); ++j)
|
|
|
|
{
|
|
|
|
// Switch to regular colours after all selected syllables
|
|
|
|
if (j == source_sel_length)
|
|
|
|
{
|
|
|
|
dc.SetTextBackground(inner_back);
|
|
|
|
dc.SetTextForeground(inner_text);
|
|
|
|
dc.SetBrush(wxBrush(inner_back));
|
|
|
|
}
|
|
|
|
|
|
|
|
syl_x += DrawBoxedText(dc, unmatched_source[j].text, syl_x, y_line1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remaining destination
|
|
|
|
wxString txt = unmatched_destination.Left(destination_sel_length);
|
2011-09-28 21:44:07 +02:00
|
|
|
if (!txt.empty())
|
|
|
|
{
|
|
|
|
dc.SetTextBackground(sel_back);
|
|
|
|
dc.SetTextForeground(sel_text);
|
|
|
|
dc.SetBrush(wxBrush(sel_back));
|
2009-06-08 04:37:09 +02:00
|
|
|
next_x += DrawBoxedText(dc, txt, next_x, y_line2);
|
2011-09-28 21:44:07 +02:00
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
txt = unmatched_destination.Mid(destination_sel_length);
|
2011-09-28 21:44:07 +02:00
|
|
|
if (!txt.empty())
|
|
|
|
{
|
|
|
|
dc.SetTextBackground(inner_back);
|
|
|
|
dc.SetTextForeground(inner_text);
|
|
|
|
dc.SetBrush(wxBrush(inner_back));
|
2009-06-08 04:37:09 +02:00
|
|
|
DrawBoxedText(dc, txt, next_x, y_line2);
|
2011-09-28 21:44:07 +02:00
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
void KaraokeLineMatchDisplay::SetInputData(AssDialogue *src, AssDialogue *dst)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
last_total_matchgroup_render_width = 0;
|
|
|
|
|
|
|
|
matched_groups.clear();
|
|
|
|
|
|
|
|
unmatched_source.clear();
|
|
|
|
source_sel_length = 0;
|
|
|
|
if (src)
|
|
|
|
{
|
2011-09-28 21:44:07 +02:00
|
|
|
AssKaraoke kara(src);
|
|
|
|
copy(kara.begin(), kara.end(), back_inserter(unmatched_source));
|
|
|
|
source_sel_length = 1;
|
2009-06-08 04:37:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unmatched_destination.clear();
|
|
|
|
destination_sel_length = 0;
|
|
|
|
if (dst)
|
|
|
|
{
|
|
|
|
unmatched_destination = dst->GetStrippedText();
|
2011-09-28 21:44:07 +02:00
|
|
|
if (!unmatched_destination.empty())
|
|
|
|
{
|
|
|
|
destination_sel_length = 1;
|
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Refresh(true);
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
wxString KaraokeLineMatchDisplay::GetOutputLine() const
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
wxString res;
|
|
|
|
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto const& match : matched_groups)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2011-09-28 21:44:07 +02:00
|
|
|
int duration = 0;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto const& syl : match.src)
|
|
|
|
duration += syl.duration;
|
2012-03-27 02:48:56 +02:00
|
|
|
res = wxString::Format("%s{\\k%d}%s", res, duration / 10, match.dst);
|
2009-06-08 04:37:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KaraokeLineMatchDisplay::IncreaseSourceMatch()
|
|
|
|
{
|
2011-09-28 21:44:07 +02:00
|
|
|
source_sel_length = std::min(source_sel_length + 1, GetRemainingSource());
|
2009-06-08 04:37:09 +02:00
|
|
|
Refresh(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KaraokeLineMatchDisplay::DecreaseSourceMatch()
|
|
|
|
{
|
2011-11-07 05:14:09 +01:00
|
|
|
source_sel_length = std::max<size_t>(source_sel_length, 1) - 1;
|
2009-06-08 04:37:09 +02:00
|
|
|
Refresh(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KaraokeLineMatchDisplay::IncreseDestinationMatch()
|
|
|
|
{
|
2011-09-28 21:44:07 +02:00
|
|
|
destination_sel_length = std::min(destination_sel_length + 1, GetRemainingDestination());
|
2009-06-08 04:37:09 +02:00
|
|
|
Refresh(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KaraokeLineMatchDisplay::DecreaseDestinationMatch()
|
|
|
|
{
|
2011-11-07 05:14:09 +01:00
|
|
|
destination_sel_length = std::max<size_t>(destination_sel_length, 1) - 1;
|
2009-06-08 04:37:09 +02:00
|
|
|
Refresh(true);
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
/// Kana interpolation, in characters, unset to disable
|
|
|
|
#define KANA_SEARCH_DISTANCE 3
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
void KaraokeLineMatchDisplay::AutoMatchJapanese()
|
|
|
|
{
|
|
|
|
if (unmatched_source.size() < 1) return;
|
|
|
|
|
|
|
|
// Quick escape: If there's no destination left, take all remaining source.
|
|
|
|
// (Usually this means the user made a mistake.)
|
2011-09-28 21:44:07 +02:00
|
|
|
if (unmatched_destination.empty())
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
source_sel_length = unmatched_source.size();
|
|
|
|
destination_sel_length = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll first see if we can do something with the first unmatched source syllable
|
|
|
|
wxString src(unmatched_source[0].text.Lower());
|
|
|
|
wxString dst(unmatched_destination);
|
|
|
|
source_sel_length = 1; // we're working on the first, assume it was matched
|
|
|
|
destination_sel_length = 0;
|
|
|
|
|
|
|
|
// Quick escape: If the source syllable is empty, return with first source syllable and empty destination
|
2011-09-28 21:44:07 +02:00
|
|
|
if (src.empty())
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
// Try to match the next source syllable against the destination. Do it
|
|
|
|
// "inverted": try all kana from the table and prefix-match them against
|
|
|
|
// the destination, then if it matches a prefix, try to match the hepburn
|
|
|
|
// for it agast the source; eat if it matches. Keep trying to match as
|
|
|
|
// long as there's text left in the source syllable or matching fails.
|
2009-06-08 04:37:09 +02:00
|
|
|
while (src.size() > 0)
|
|
|
|
{
|
|
|
|
wxString dst_hira_rest, dst_kata_rest, src_rest;
|
|
|
|
bool matched = false;
|
2012-03-20 01:39:10 +01:00
|
|
|
for (const KanaEntry *ke = KanaTable; ke->hiragana; ++ke)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2011-09-28 21:44:07 +02:00
|
|
|
if (src.StartsWith(ke->hepburn, &src_rest))
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2011-09-28 21:44:07 +02:00
|
|
|
bool hira_matches = dst.StartsWith(ke->hiragana, &dst_hira_rest) && *ke->hiragana;
|
|
|
|
bool kata_matches = dst.StartsWith(ke->katakana, &dst_kata_rest);
|
|
|
|
|
|
|
|
if (hira_matches || kata_matches)
|
|
|
|
{
|
|
|
|
matched = true;
|
|
|
|
src = src_rest;
|
|
|
|
dst = hira_matches ? dst_hira_rest : dst_kata_rest;
|
|
|
|
destination_sel_length += wcslen(hira_matches ? ke->hiragana : ke->katakana);
|
|
|
|
break;
|
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!matched) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The source might be empty now: That's good!
|
|
|
|
// That means we managed to match it all against destination text
|
2011-09-28 21:44:07 +02:00
|
|
|
if (src.empty())
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
// destination_sel_length already has the appropriate value
|
|
|
|
// and source_sel_length was alredy 1
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now the source syllable might consist of just whitespace.
|
|
|
|
// Eat all whitespace at the start of the destination.
|
2009-06-25 00:22:45 +02:00
|
|
|
if (StringEmptyOrWhitespace(src))
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2012-07-21 05:50:35 +02:00
|
|
|
while (destination_sel_length < unmatched_destination.size() && IsWhitespace(unmatched_destination[destination_sel_length]))
|
2011-09-28 21:44:07 +02:00
|
|
|
++destination_sel_length;
|
2009-06-08 04:37:09 +02:00
|
|
|
// Now we've eaten all spaces in the destination as well
|
|
|
|
// so the selection lengths should be good
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's just one character left in the destination at this point,
|
|
|
|
// (and the source doesn't begin with space syllables, see test above)
|
|
|
|
// assume it's safe to take all remaining source to match the single
|
|
|
|
// remaining destination.
|
|
|
|
if (unmatched_destination.size() == 1)
|
|
|
|
{
|
|
|
|
source_sel_length = unmatched_source.size();
|
|
|
|
destination_sel_length = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef KANA_SEARCH_DISTANCE
|
2011-09-28 21:44:07 +02:00
|
|
|
// Try to look up to KANA_SEARCH_DISTANCE characters ahead in destination,
|
|
|
|
// see if any of those are recognised kana. If there are any within the
|
|
|
|
// range, see if it matches a following syllable, at most 5 source
|
|
|
|
// syllables per character in source we're ahead.
|
|
|
|
// The number 5 comes from the kanji with the longest readings:
|
|
|
|
// 'uketamawa.ru' and 'kokorozashi' which each have a reading consisting of
|
|
|
|
// five kana.
|
|
|
|
// Only match the found kana in destination against the beginning of source
|
|
|
|
// syllables, not the middle of them.
|
|
|
|
// If a match is found, make a guess at how much source and destination
|
|
|
|
// should be selected based on the distances it was found at.
|
2009-06-08 04:37:09 +02:00
|
|
|
dst = unmatched_destination;
|
|
|
|
for (size_t lookahead = 0; lookahead < KANA_SEARCH_DISTANCE; ++lookahead)
|
|
|
|
{
|
|
|
|
// Eat dst at the beginning, don't test for the first character being kana
|
|
|
|
dst = dst.Mid(1);
|
|
|
|
// Find a position where hiragana or katakana matches
|
|
|
|
wxString matched_roma;
|
|
|
|
wxString matched_kana;
|
2012-03-20 01:39:10 +01:00
|
|
|
for (const KanaEntry *ke = KanaTable; ke->hiragana; ++ke)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2011-09-28 21:44:07 +02:00
|
|
|
if (*ke->hiragana && dst.StartsWith(ke->hiragana))
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
matched_roma = ke->hepburn;
|
|
|
|
matched_kana = ke->hiragana;
|
|
|
|
break;
|
|
|
|
}
|
2011-09-28 21:44:07 +02:00
|
|
|
if (*ke->katakana && dst.StartsWith(ke->katakana))
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
matched_roma = ke->hepburn;
|
|
|
|
matched_kana = ke->katakana;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we didn't match any kana against dst, move to next char in dst
|
|
|
|
if (!matched_kana)
|
|
|
|
continue;
|
|
|
|
// Otherwise look for a match for the romaji
|
|
|
|
// For the magic number 5, see big comment block above
|
|
|
|
int src_lookahead_max = (lookahead+1)*5;
|
|
|
|
int src_lookahead_pos = 0;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto const& syl : unmatched_source)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
// Check if we've gone too far ahead in the source
|
|
|
|
if (src_lookahead_pos++ >= src_lookahead_max) break;
|
|
|
|
// Otherwise look for a match
|
2012-11-04 04:53:03 +01:00
|
|
|
if (syl.text.StartsWith(matched_roma))
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
|
|
|
// Yay! Time to interpolate.
|
|
|
|
// Special case: If the last source syllable before the matching one is
|
|
|
|
// empty or contains just whitespace, don't include that one.
|
2009-06-25 00:22:45 +02:00
|
|
|
if (src_lookahead_pos > 1 && StringEmptyOrWhitespace(unmatched_source[src_lookahead_pos-2].text))
|
2009-06-08 04:37:09 +02:00
|
|
|
src_lookahead_pos -= 1;
|
|
|
|
// Special case: Just one source syllable matching, pick all destination found
|
|
|
|
if (src_lookahead_pos == 2)
|
|
|
|
{
|
|
|
|
source_sel_length = 1;
|
|
|
|
destination_sel_length = lookahead+1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Otherwise try to split the eaten source syllables evenly between the eaten
|
|
|
|
// destination characters, and do a regular rounding.
|
|
|
|
float src_per_dst = (float)(src_lookahead_pos-1)/(float)(lookahead+1);
|
|
|
|
source_sel_length = (int)(src_per_dst + 0.5);
|
|
|
|
destination_sel_length = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Okay so we didn't match anything. Aww.
|
|
|
|
// Just fail...
|
|
|
|
// We know from earlier that we do have both some source and some destination.
|
|
|
|
source_sel_length = 1;
|
|
|
|
destination_sel_length = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KaraokeLineMatchDisplay::AcceptMatch()
|
|
|
|
{
|
|
|
|
MatchGroup match;
|
|
|
|
|
|
|
|
if (source_sel_length == 0 && destination_sel_length == 0)
|
|
|
|
{
|
|
|
|
// Completely empty match
|
|
|
|
return false;
|
|
|
|
}
|
2010-06-11 04:24:59 +02:00
|
|
|
|
|
|
|
assert(source_sel_length <= unmatched_source.size());
|
2011-09-28 21:44:07 +02:00
|
|
|
copy(unmatched_source.begin(), unmatched_source.begin() + source_sel_length, back_inserter(match.src));
|
2012-02-28 02:23:07 +01:00
|
|
|
unmatched_source.erase(unmatched_source.begin(), unmatched_source.begin() + source_sel_length);
|
2011-09-28 21:44:07 +02:00
|
|
|
source_sel_length = 0;
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2010-06-11 04:24:59 +02:00
|
|
|
assert(destination_sel_length <= unmatched_destination.size());
|
2009-06-08 04:37:09 +02:00
|
|
|
match.dst = unmatched_destination.Left(destination_sel_length);
|
|
|
|
unmatched_destination = unmatched_destination.Mid(destination_sel_length);
|
|
|
|
destination_sel_length = 0;
|
|
|
|
|
|
|
|
matched_groups.push_back(match);
|
|
|
|
|
|
|
|
IncreaseSourceMatch();
|
|
|
|
IncreseDestinationMatch();
|
|
|
|
Refresh(true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KaraokeLineMatchDisplay::UndoMatch()
|
|
|
|
{
|
|
|
|
if (matched_groups.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
MatchGroup &group = matched_groups.back();
|
|
|
|
|
|
|
|
source_sel_length = group.src.size();
|
|
|
|
destination_sel_length = group.dst.size();
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
copy(group.src.rbegin(), group.src.rend(), front_inserter(unmatched_source));
|
|
|
|
group.src.clear();
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
unmatched_destination = group.dst + unmatched_destination;
|
|
|
|
|
|
|
|
matched_groups.pop_back();
|
|
|
|
|
|
|
|
Refresh(true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-16 08:17:36 +01:00
|
|
|
DialogKanjiTimer::DialogKanjiTimer(agi::Context *c)
|
2012-10-12 19:16:39 +02:00
|
|
|
: wxDialog(c->parent, -1, _("Kanji timing"))
|
|
|
|
, subs(c->ass)
|
2012-11-24 20:35:51 +01:00
|
|
|
, currentSourceLine(nullptr)
|
|
|
|
, currentDestinationLine(nullptr)
|
2007-01-13 03:22:28 +01:00
|
|
|
{
|
2012-04-03 22:40:24 +02:00
|
|
|
SetIcon(GETICON(kara_timing_copier_16));
|
2007-07-05 01:09:40 +02:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
wxSizer *DisplayBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Text"));
|
|
|
|
wxSizer *StylesBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Styles"));
|
|
|
|
wxFlexGridSizer *StylesGridSizer = new wxFlexGridSizer(2, 2, 6, 6);
|
|
|
|
wxSizer *HelpBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Shortcut Keys"));
|
|
|
|
wxSizer *ButtonsBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Commands"));
|
|
|
|
wxSizer *MainStackSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
wxSizer *BottomShelfSizer = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
wxSizer *BottomLeftStackSizer = new wxBoxSizer(wxVERTICAL);
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
display = new KaraokeLineMatchDisplay(this);
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
//Checkbox
|
2011-11-18 19:49:09 +01:00
|
|
|
Interpolate = new wxCheckBox(this,-1,_("Attempt to &interpolate kanji."),wxDefaultPosition,wxDefaultSize,wxALIGN_LEFT);
|
2010-05-21 03:13:36 +02:00
|
|
|
Interpolate->SetValue(OPT_GET("Tool/Kanji Timer/Interpolation")->GetBool());
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
wxArrayString styles = subs->GetStyles();
|
2012-03-27 02:48:28 +02:00
|
|
|
SourceStyle = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(160, -1), styles, wxCB_READONLY);
|
|
|
|
DestStyle = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(160, -1), styles, wxCB_READONLY);
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
wxStaticText *ShortcutKeys = new wxStaticText(this,-1,_("When the destination textbox has focus, use the following keys:\n\nRight Arrow: Increase dest. selection length\nLeft Arrow: Decrease dest. selection length\nUp Arrow: Increase source selection length\nDown Arrow: Decrease source selection length\nEnter: Link, accept line when done\nBackspace: Unlink last"));
|
2007-01-13 03:42:19 +01:00
|
|
|
|
2007-01-13 03:22:28 +01:00
|
|
|
//Buttons
|
2011-11-18 19:49:09 +01:00
|
|
|
wxButton *Start = new wxButton(this,BUTTON_KTSTART,_("S&tart!"));
|
|
|
|
wxButton *Link = new wxButton(this,BUTTON_KTLINK,_("&Link"));
|
|
|
|
wxButton *Unlink = new wxButton(this,BUTTON_KTUNLINK,_("&Unlink"));
|
|
|
|
wxButton *SkipSourceLine = new wxButton(this,BUTTON_KTSKIPSOURCE,_("Skip &Source Line"));
|
|
|
|
wxButton *SkipDestLine = new wxButton(this,BUTTON_KTSKIPDEST,_("Skip &Dest Line"));
|
|
|
|
wxButton *GoBackLine = new wxButton(this,BUTTON_KTGOBACK,_("&Go Back a Line"));
|
|
|
|
wxButton *AcceptLine = new wxButton(this,BUTTON_KTACCEPT,_("&Accept Line"));
|
|
|
|
wxButton *CloseKT = new wxButton(this,wxID_CLOSE,_("&Close"));
|
2007-01-15 00:34:27 +01:00
|
|
|
|
2007-01-13 03:22:28 +01:00
|
|
|
//Frame: Text
|
2009-06-08 04:37:09 +02:00
|
|
|
DisplayBoxSizer->Add(display, 0, wxEXPAND|wxALL, 6);
|
|
|
|
DisplayBoxSizer->Add(Interpolate, 0, wxEXPAND|wxALL, 6);
|
2007-01-13 03:22:28 +01:00
|
|
|
//Frame: Styles
|
2009-06-08 04:37:09 +02:00
|
|
|
StylesGridSizer->Add(new wxStaticText(this, -1, TEXT_LABEL_SOURCE), 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
|
|
|
|
StylesGridSizer->Add(SourceStyle, 1, wxEXPAND);
|
|
|
|
StylesGridSizer->Add(new wxStaticText(this, -1, TEXT_LABEL_DEST), 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
|
|
|
|
StylesGridSizer->Add(DestStyle, 1, wxEXPAND);
|
|
|
|
StylesBoxSizer->Add(StylesGridSizer, 1, wxEXPAND|wxALL, 6);
|
|
|
|
//Frame: Shortcut Keys
|
|
|
|
HelpBoxSizer->Add(ShortcutKeys, 1, wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 6);
|
2007-01-13 03:22:28 +01:00
|
|
|
//Frame: Commands
|
2009-06-08 04:37:09 +02:00
|
|
|
ButtonsBoxSizer->AddStretchSpacer(1);
|
|
|
|
ButtonsBoxSizer->Add(Start, 0, wxEXPAND|wxALL, 6);
|
|
|
|
ButtonsBoxSizer->Add(Link, 0, wxEXPAND|(wxALL&~wxTOP), 6);
|
|
|
|
ButtonsBoxSizer->Add(Unlink, 0, wxEXPAND|(wxALL&~wxTOP), 6);
|
|
|
|
ButtonsBoxSizer->Add(SkipSourceLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
|
|
|
|
ButtonsBoxSizer->Add(SkipDestLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
|
|
|
|
ButtonsBoxSizer->Add(GoBackLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
|
|
|
|
ButtonsBoxSizer->Add(AcceptLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
|
|
|
|
ButtonsBoxSizer->AddStretchSpacer(1);
|
2007-10-31 22:52:51 +01:00
|
|
|
|
|
|
|
// Button sizer
|
|
|
|
wxStdDialogButtonSizer *buttonSizer = new wxStdDialogButtonSizer();
|
2011-09-28 21:43:11 +02:00
|
|
|
buttonSizer->AddButton(new HelpButton(this,"Kanji Timer"));
|
2007-10-31 22:52:51 +01:00
|
|
|
buttonSizer->SetAffirmativeButton(CloseKT);
|
|
|
|
buttonSizer->Realize();
|
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
// Layout it all
|
|
|
|
BottomLeftStackSizer->Add(StylesBoxSizer, 0, wxEXPAND|wxBOTTOM, 6);
|
|
|
|
BottomLeftStackSizer->Add(HelpBoxSizer, 1, wxEXPAND, 6);
|
|
|
|
BottomShelfSizer->Add(BottomLeftStackSizer, 1, wxEXPAND|wxRIGHT, 6);
|
|
|
|
BottomShelfSizer->Add(ButtonsBoxSizer, 0, wxEXPAND, 6);
|
|
|
|
MainStackSizer->Add(DisplayBoxSizer, 0, wxEXPAND|wxALL, 6);
|
|
|
|
MainStackSizer->Add(BottomShelfSizer, 1, wxEXPAND|wxLEFT|wxRIGHT, 6);
|
|
|
|
MainStackSizer->Add(buttonSizer, 0, wxEXPAND|wxALL, 6);
|
|
|
|
|
|
|
|
SetSizerAndFit(MainStackSizer);
|
2007-01-13 03:22:28 +01:00
|
|
|
CenterOnParent();
|
|
|
|
|
2011-09-28 21:44:07 +02:00
|
|
|
display->Bind(wxEVT_KEY_DOWN, &DialogKanjiTimer::OnKeyDown, this);
|
|
|
|
}
|
2007-01-13 03:22:28 +01:00
|
|
|
|
|
|
|
BEGIN_EVENT_TABLE(DialogKanjiTimer,wxDialog)
|
2007-01-15 00:34:27 +01:00
|
|
|
EVT_BUTTON(wxID_CLOSE,DialogKanjiTimer::OnClose)
|
2007-01-13 03:22:28 +01:00
|
|
|
EVT_BUTTON(BUTTON_KTSTART,DialogKanjiTimer::OnStart)
|
|
|
|
EVT_BUTTON(BUTTON_KTLINK,DialogKanjiTimer::OnLink)
|
|
|
|
EVT_BUTTON(BUTTON_KTUNLINK,DialogKanjiTimer::OnUnlink)
|
|
|
|
EVT_BUTTON(BUTTON_KTSKIPSOURCE,DialogKanjiTimer::OnSkipSource)
|
|
|
|
EVT_BUTTON(BUTTON_KTSKIPDEST,DialogKanjiTimer::OnSkipDest)
|
|
|
|
EVT_BUTTON(BUTTON_KTGOBACK,DialogKanjiTimer::OnGoBack)
|
|
|
|
EVT_BUTTON(BUTTON_KTACCEPT,DialogKanjiTimer::OnAccept)
|
|
|
|
EVT_KEY_DOWN(DialogKanjiTimer::OnKeyDown)
|
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnClose(wxCommandEvent &) {
|
2010-05-21 03:13:36 +02:00
|
|
|
OPT_SET("Tool/Kanji Timer/Interpolation")->SetBool(Interpolate->IsChecked());
|
2011-09-28 21:44:07 +02:00
|
|
|
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto& line : LinesToChange)
|
|
|
|
line.first->Text = line.second;
|
2011-09-28 21:44:07 +02:00
|
|
|
|
|
|
|
if (LinesToChange.size()) {
|
2011-09-15 07:16:32 +02:00
|
|
|
subs->Commit(_("kanji timing"), AssFile::COMMIT_DIAG_TEXT);
|
2007-03-08 19:57:55 +01:00
|
|
|
LinesToChange.clear();
|
|
|
|
}
|
2007-01-15 00:34:27 +01:00
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnStart(wxCommandEvent &) {
|
2011-09-28 21:44:07 +02:00
|
|
|
if (SourceStyle->GetValue().empty() || DestStyle->GetValue().empty())
|
2007-01-13 08:04:12 +01:00
|
|
|
wxMessageBox(_("Select source and destination styles first."),_("Error"),wxICON_EXCLAMATION | wxOK);
|
|
|
|
else if (SourceStyle->GetValue() == DestStyle->GetValue())
|
|
|
|
wxMessageBox(_("The source and destination styles must be different."),_("Error"),wxICON_EXCLAMATION | wxOK);
|
|
|
|
else {
|
2012-10-12 19:16:39 +02:00
|
|
|
currentSourceLine = FindNextStyleMatch(&*subs->Line.begin(), SourceStyle->GetValue());
|
|
|
|
currentDestinationLine = FindNextStyleMatch(&*subs->Line.begin(), DestStyle->GetValue());
|
2009-06-08 04:37:09 +02:00
|
|
|
ResetForNewLine();
|
2007-01-13 08:04:12 +01:00
|
|
|
}
|
2007-01-25 23:47:29 +01:00
|
|
|
LinesToChange.clear();
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnLink(wxCommandEvent &) {
|
2009-06-08 04:37:09 +02:00
|
|
|
if (display->AcceptMatch())
|
|
|
|
TryAutoMatch();
|
|
|
|
else
|
|
|
|
wxBell();
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnUnlink(wxCommandEvent &) {
|
2009-06-08 04:37:09 +02:00
|
|
|
if (!display->UndoMatch())
|
|
|
|
wxBell();
|
|
|
|
// Don't auto-match here, undoing sets the selection to the undone match
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
2007-01-13 08:04:12 +01:00
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnSkipSource(wxCommandEvent &) {
|
2009-06-08 04:37:09 +02:00
|
|
|
currentSourceLine = FindNextStyleMatch(currentSourceLine, SourceStyle->GetValue());
|
|
|
|
ResetForNewLine();
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
2007-01-24 04:54:32 +01:00
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnSkipDest(wxCommandEvent &) {
|
2009-06-08 04:37:09 +02:00
|
|
|
currentDestinationLine = FindNextStyleMatch(currentDestinationLine, DestStyle->GetValue());
|
|
|
|
ResetForNewLine();
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnGoBack(wxCommandEvent &) {
|
2011-09-28 21:44:07 +02:00
|
|
|
if (LinesToChange.size())
|
2007-01-25 23:47:29 +01:00
|
|
|
LinesToChange.pop_back(); //If we go back, then take out the modified line we saved.
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
currentSourceLine = FindPrevStyleMatch(currentSourceLine, SourceStyle->GetValue());
|
|
|
|
currentDestinationLine = FindPrevStyleMatch(currentDestinationLine, DestStyle->GetValue());
|
|
|
|
ResetForNewLine();
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2011-12-22 22:09:31 +01:00
|
|
|
void DialogKanjiTimer::OnAccept(wxCommandEvent &) {
|
2012-10-12 19:16:39 +02:00
|
|
|
if (!currentDestinationLine) return;
|
2012-03-27 02:48:47 +02:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
if (display->GetRemainingSource() > 0)
|
2007-01-13 08:04:12 +01:00
|
|
|
wxMessageBox(_("Group all of the source text."),_("Error"),wxICON_EXCLAMATION | wxOK);
|
2012-10-12 19:16:39 +02:00
|
|
|
else if (AssDialogue *destLine = dynamic_cast<AssDialogue*>(currentDestinationLine)) {
|
2012-03-27 02:48:47 +02:00
|
|
|
LinesToChange.push_back(std::make_pair(destLine, display->GetOutputLine()));
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
currentSourceLine = FindNextStyleMatch(currentSourceLine, SourceStyle->GetValue());
|
|
|
|
currentDestinationLine = FindNextStyleMatch(currentDestinationLine, DestStyle->GetValue());
|
|
|
|
ResetForNewLine();
|
2007-01-13 08:04:12 +01:00
|
|
|
}
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2007-01-13 03:22:28 +01:00
|
|
|
void DialogKanjiTimer::OnKeyDown(wxKeyEvent &event) {
|
2007-01-24 04:54:32 +01:00
|
|
|
wxCommandEvent evt;
|
2007-01-16 21:55:27 +01:00
|
|
|
switch(event.GetKeyCode()) {
|
2007-01-13 03:22:28 +01:00
|
|
|
case WXK_ESCAPE :
|
2007-01-24 04:54:32 +01:00
|
|
|
OnClose(evt);
|
2007-01-13 03:22:28 +01:00
|
|
|
break;
|
|
|
|
case WXK_BACK :
|
2007-01-24 04:54:32 +01:00
|
|
|
OnUnlink(evt);
|
2007-01-13 03:22:28 +01:00
|
|
|
break;
|
|
|
|
case WXK_RIGHT : //inc dest selection len
|
2009-06-08 04:37:09 +02:00
|
|
|
display->IncreseDestinationMatch();
|
2007-01-13 03:22:28 +01:00
|
|
|
break;
|
|
|
|
case WXK_LEFT : //dec dest selection len
|
2009-06-08 04:37:09 +02:00
|
|
|
display->DecreaseDestinationMatch();
|
2007-01-13 03:22:28 +01:00
|
|
|
break;
|
|
|
|
case WXK_UP : //inc source selection len
|
2009-06-08 04:37:09 +02:00
|
|
|
display->IncreaseSourceMatch();
|
2007-01-13 03:22:28 +01:00
|
|
|
break;
|
|
|
|
case WXK_DOWN : //dec source selection len
|
2009-06-08 04:37:09 +02:00
|
|
|
display->DecreaseSourceMatch();
|
2007-01-13 03:22:28 +01:00
|
|
|
break;
|
|
|
|
case WXK_RETURN :
|
2012-03-27 02:48:38 +02:00
|
|
|
if (display->GetRemainingSource() == 0 && display->GetRemainingDestination() == 0)
|
|
|
|
OnAccept(evt);
|
|
|
|
else
|
|
|
|
OnLink(evt);
|
2007-01-13 03:22:28 +01:00
|
|
|
break;
|
|
|
|
default :
|
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
}
|
2009-06-08 04:37:09 +02:00
|
|
|
|
|
|
|
void DialogKanjiTimer::ResetForNewLine()
|
|
|
|
{
|
2012-11-24 20:35:51 +01:00
|
|
|
AssDialogue *src = nullptr;
|
|
|
|
AssDialogue *dst = nullptr;
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2012-10-12 19:16:39 +02:00
|
|
|
if (currentSourceLine)
|
|
|
|
src = dynamic_cast<AssDialogue*>(currentSourceLine);
|
|
|
|
if (currentDestinationLine)
|
|
|
|
dst = dynamic_cast<AssDialogue*>(currentDestinationLine);
|
2009-06-08 04:37:09 +02:00
|
|
|
|
2012-11-24 20:35:51 +01:00
|
|
|
if (!src || !dst)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2012-11-24 20:35:51 +01:00
|
|
|
src = dst = nullptr;
|
2009-06-08 04:37:09 +02:00
|
|
|
wxBell();
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
display->SetInputData(src, dst);
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
TryAutoMatch();
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
display->SetFocus();
|
|
|
|
}
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2009-06-08 04:37:09 +02:00
|
|
|
void DialogKanjiTimer::TryAutoMatch()
|
|
|
|
{
|
|
|
|
if (Interpolate->GetValue())
|
|
|
|
display->AutoMatchJapanese();
|
|
|
|
}
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2012-11-24 20:35:51 +01:00
|
|
|
template<typename Iterator>
|
|
|
|
static AssEntry *find_next(Iterator from, Iterator to, wxString const& style_name) {
|
|
|
|
for (++from; from != to; ++from)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2012-11-24 20:35:51 +01:00
|
|
|
AssDialogue *dlg = dynamic_cast<AssDialogue*>(&*from);
|
|
|
|
if (dlg && dlg->Style == style_name)
|
2012-10-12 19:16:39 +02:00
|
|
|
return dlg;
|
2009-06-08 04:37:09 +02:00
|
|
|
}
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2012-11-24 20:35:51 +01:00
|
|
|
return nullptr;
|
2007-01-13 03:22:28 +01:00
|
|
|
}
|
|
|
|
|
2012-11-24 20:35:51 +01:00
|
|
|
AssEntry *DialogKanjiTimer::FindNextStyleMatch(AssEntry *search_from, const wxString &search_style)
|
2009-06-08 04:37:09 +02:00
|
|
|
{
|
2012-10-12 19:16:39 +02:00
|
|
|
if (!search_from) return search_from;
|
2012-11-24 20:35:51 +01:00
|
|
|
return find_next(subs->Line.iterator_to(*search_from), subs->Line.end(), search_style);
|
|
|
|
}
|
2007-01-13 03:22:28 +01:00
|
|
|
|
2012-11-24 20:35:51 +01:00
|
|
|
AssEntry *DialogKanjiTimer::FindPrevStyleMatch(AssEntry *search_from, const wxString &search_style)
|
|
|
|
{
|
|
|
|
if (!search_from) return search_from;
|
|
|
|
return find_next(EntryList::reverse_iterator(subs->Line.iterator_to(*search_from)), subs->Line.rend(), search_style);
|
2007-01-13 08:04:12 +01:00
|
|
|
}
|