/* * Copyright (C) 2004-2005 Vadim Berezniker * http://www.kryptolus.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "common.h" #include "pavl.h" #include "sabbu.h" #include "gui_event_list.h" extern struct sabbu app; /* * This class represents a subtitle script. */ void kryScript::Init() { this->m_styles = new kryHash(g_str_hash, g_str_equal, kry_free_minimal, (GDestroyNotify) kryStyle::DestroyStatic); this->m_names = new kryHash(g_str_hash, g_str_equal, kry_free_minimal, NULL); this->m_properties = new kryHash(g_str_hash, g_str_equal, kry_free_minimal, kry_free_minimal); this->m_tree_events = NULL; if(app.opts.disable_collisions) this->m_disable_collisions = TRUE; } /* * Creates an empty script. */ kryScript::kryScript() : kryObject(kryScript::SIGNAL_COUNT), m_disable_collisions(FALSE), m_encoding(ENCODING_ASCII), m_modifiedFlag(FALSE), m_filename(NULL), m_filename_suggested(NULL), m_blank_count(0), m_id(0), m_script_type(KRY_FORMAT_SSA) { this->Init(); } /* * Destroys the script */ kryScript::~kryScript() { if(this->m_filename) kry_free(this->m_filename); if(this->m_filename_suggested) kry_free(this->m_filename_suggested); delete this->m_properties; delete this->m_styles; delete this->m_names; kryListIterator iter; this->m_events.GetIterator(&iter); kryEvent *line; while((line = iter.GetNext())) delete line; if(this->m_tree_events) pavl_destroy(this->m_tree_events, kryScript::EventNodeFree); } /* * Compares two event nodes and determines their order based on event start time. */ int kryScript::EventNodeCompare(const void *a, const void *b, void * param) { struct event_node_data *data_left = (struct event_node_data *) a; struct event_node_data *data_right = (struct event_node_data *) b; kryEvent *event_left = (kryEvent *) data_left->events->data; kryEvent *event_right = (kryEvent *) data_right->events->data; if(event_left->GetStart() < event_right->GetStart()) return -1; else if(event_left->GetStart() > event_right->GetStart()) return 1; else return 0; } /* * Frees memory used by the event node. */ void kryScript::EventNodeFree(void *a, void *param) { struct event_node_data *data = (struct event_node_data*) a; g_list_free(data->events); delete data; } /* * Returns the style identified by name or NULL if not found. */ kryStyle *kryScript::GetStyle(char *name) { return this->m_styles->Lookup(name); } /* * Returns an iterator for the list of style names. */ void kryScript::GetStyleNameIterator(kryListIterator *iter) { this->m_styles->GetKeysIterator(iter); } /* * Adds a default style to the script. */ void kryScript::AddDefaultStyle() { kryStyle *style = new kryStyle(); if(this->GetType() == KRY_FORMAT_ASS) style->SetType(KRY_STYLE_ASS); char *name_copy = kry_strdup(style->GetName()); this->m_styles->Insert(name_copy, style); this->SetModifiedFlag(TRUE); } /* * Adds the given style to the script. */ void kryScript::AddStyle(kryStyle *style) { if(!style) { g_warning("Attempt to add NULL style to script."); return; } this->m_styles->Insert(kry_strdup(style->GetName()), style); this->SetModifiedFlag(TRUE); } /* * Removes the style with the given name from the script. */ void kryScript::RemoveStyle(char *key) { this->m_styles->Remove(key); this->SetModifiedFlag(TRUE); } /* * Gets the value of the given property or NULL if the property was not found. */ char *kryScript::GetProperty(char *key) { return this->m_properties->Lookup(key); } /* * Sets the given property. */ void kryScript::SetProperty(char *key, char *value) { if(!value) this->m_properties->Remove(key); else this->m_properties->Replace(key, value); this->SetModifiedFlag(TRUE); } /* * Returns an Iterator for the names of all the properties in the script. */ void kryScript::GetPropertyNameIterator(kryListIterator *iter) { this->m_properties->GetKeysIterator(iter); } /* * Alters the "modifed" flag of the script. */ void kryScript::SetModifiedFlag(gboolean modified) { this->InvokeSignal(SIGNAL_MODIFIED, (void *) modified); this->m_modifiedFlag = modified; } /* * Returns the "modified" flag of the script. */ gboolean kryScript::GetModifiedFlag() { return this->m_modifiedFlag; } /* * Gets the type of the script. */ enum kryScriptType kryScript::GetType() { return this->m_script_type; } /* * Sets the "V4+" (ASS) flag of the script. */ void kryScript::SetType(enum kryScriptType type) { if(this->m_script_type != type) this->InvokeSignal(SIGNAL_TYPE_CHANGED, (void *) type); this->m_script_type = type; } /* * Returns the filename of the script. */ char *kryScript::GetFilename() { return this->m_filename; } /* * Sets the filename of the script. */ void kryScript::SetFilename(char *filename) { if(this->m_filename) kry_free(this->m_filename); this->m_filename = kry_strdup(filename); } /* * Returns the filename that will be suggested when an untitled file is saved. */ char *kryScript::GetSuggestedFilename() { return this->m_filename_suggested; } /* * Sets the filename that will be suggested when an untitled file is saved. */ void kryScript::SetSuggestedFilename(char *text) { if(this->m_filename_suggested) kry_free(this->m_filename_suggested); this->m_filename_suggested = kry_strdup(text); } /* * Removes all of the events in the script from the Event Tree. */ /* * Disables detection of overlapping events within the script. */ void kryScript::DisableCollisions() { /*if(!this->m_disable_collisions) { this->m_disable_collisions = TRUE; this->EventTreeRemoveAll(); }*/ } /* * Enables the detection of overlapping events within the script. */ void kryScript::EnableCollisions() { /*if(this->m_disable_collisions) { this->m_disable_collisions = FALSE; this->EventTreeAddAll(); }*/ } /* * Hooks to modified signals of the given event. */ void kryScript::HookSignals(kryEventDetailed *event) { if(!event) { g_warning("NULL event object passed to HookSignals"); return; } event->ConnectSignal(kryEvent::SIGNAL_BEFORE_TIME_CHANGED, (krySignalFunc1) this->BeforeModifyEventW, this); event->ConnectSignal(kryEvent::SIGNAL_AFTER_TIME_CHANGED, (krySignalFunc1) this->AfterModifyEventW, this); event->ConnectSignal(kryEvent::SIGNAL_BEFORE_LAYER_CHANGED, (krySignalFunc1) this->BeforeModifyEventW, this); event->ConnectSignal(kryEvent::SIGNAL_AFTER_LAYER_CHANGED, (krySignalFunc1) this->AfterModifyEventW, this); event->ConnectSignal(kryEvent::SIGNAL_BEFORE_NAME_CHANGED, (krySignalFunc1) this->BeforeModifyNameW, this); event->ConnectSignal(kryEvent::SIGNAL_AFTER_NAME_CHANGED, (krySignalFunc1) this->AfterModifyNameW, this); } /* * Increases the reference count on the given character name. * (Used to keep track of which character names are still present in the script) */ void kryScript::NameHashAdd(char *name) { if(!name) return; int val = this->m_names->Lookup(name); this->m_names->Insert(kry_strdup(name), val+1); if(val == 0) this->InvokeSignal(kryScript::SIGNAL_NAME_ADDED, name); } /* * Decreases the reference count on the given character name. * (Used to keep track of which character names are still present in the script) */ void kryScript::NameHashRemove(char *name) { if(name == NULL) return; int val = this->m_names->Lookup(name); if(!val) { g_warning("Removing name from name hash but it is no longer there. Odd. "); return; } //g_debug("ref count of '%s' decreased to %d", name, val-1); if(val == 1) this->m_names->Remove(name); else this->m_names->Insert(kry_strdup(name), val-1); if(val == 1) this->InvokeSignal(kryScript::SIGNAL_NAME_DELETED, name); } /* * Returns an iterator of the names in the script. */ void kryScript::GetNameIterator(kryListIterator *iter) { return this->m_names->GetKeysIterator(iter); } /* * Returns the number of names in the script. */ int kryScript::GetNameCount() { return this->m_names->GetKeyCount(); } /* * Returns the nth name in the name hash. */ char *kryScript::GetNthName(int index) { kryListIterator iter; this->m_names->GetKeysIterator(&iter); int cur_idx = 0; while(char *name = iter.GetNext()) { if(cur_idx == index) return name; cur_idx++; } return NULL; } /* * Adds the given event to the script. */ void kryScript::AddEvent(kryEventDetailed *event) { if(!event) { g_warning("Attempt to add NULL event to script."); return; } event->SetIndex(this->GetEventCount()); this->m_events.Append(event); this->SetModifiedFlag(TRUE); this->NameHashAdd(event->GetName()); this->InvokeSignal(SIGNAL_EVENT_ADDED, event); this->HookSignals(event); } /* * Insert the given event at the nth position in the script. */ void kryScript::InsertEvent(kryEventDetailed *event, int n) { if(!event) { g_warning("NULL event passed to InsertEvent"); return; } else if(n < 0 || n >= this->m_events.GetLength()) { g_warning("Index passed to InsertEvent is out of range."); return; } this->m_events.Insert(event, n); this->SetModifiedFlag(TRUE); event->SetIndex(n); kryListIterator iter; this->m_events.GetNthIterator(&iter, n+1); kryEvent *ev; while((ev = iter.GetNext())) ev->SetIndex(ev->GetIndex() + 1); this->NameHashAdd(event->GetName()); this->InvokeSignal(SIGNAL_EVENT_ADDED, event); this->HookSignals(event); } /* * Gets the event at "index"th position in the script. * * Returns NULL if index is out of range. */ kryEventDetailed *kryScript::GetEvent(int index) { kryListIterator iter; this->m_events.GetNthIterator(&iter, index); return iter.GetData(); } /* * Removes the given event from the script. */ void kryScript::RemoveEvent(kryEventDetailed *event) { if(!event) { g_warning("NULL event passed to RemoveEvent"); return; } kryListIterator iter; kryEvent *line; this->NameHashRemove(event->GetName()); this->m_events.GetNthIterator(&iter, event->GetIndex()); while((line = iter.GetNext())) line->SetIndex(line->GetIndex() - 1); this->m_events.Remove(event); this->SetModifiedFlag(TRUE); this->InvokeSignal(SIGNAL_EVENT_REMOVED, event); } /* * Returns the number of blank lines at the end of the script. */ int kryScript::GetBlankCount() { return this->m_blank_count; } /* * Sets the blank count (number of blank lines at end of script). */ void kryScript::SetBlankCount(int count) { if(count < 0) { g_warning("Blank count must be positive or 0."); return; } this->m_blank_count = count; } /* * Returns the number of events in the list. */ int kryScript::GetEventCount() { return this->m_events.GetLength(); } /* * Returns the nth event in the script or NULL if the index is out of range. */ kryEventDetailed *kryScript::GetNthEvent(int n) { return this->m_events.GetNthData(n); } /* * Returns a pointer to the internal events list. */ kryList & kryScript::GetEventList() { return this->m_events; } /* * Returns an iterator for the events list. */ void kryScript::GetEventIterator(kryListIterator *iter) { this->m_events.GetIterator(iter); } /* * Returns the ID number of the script. */ int kryScript::GetID() { return this->m_id; } /* * Sets the ID number of the script. */ void kryScript::SetID(int id) { this->m_id = id; } /* * Returns an iterator for the list of all events that collide with other events. */ void kryScript::GetCollisionIterator(kryListIterator *iter) { this->m_collisions.GetIterator(iter); } /* * Debug function that prints a list of all events in the collision tree. */ void kryScript::PrintEvents() { pavl_traverser trav; pavl_t_init(&trav, this->m_tree_events); while(struct event_node_data *data = (struct event_node_data *) pavl_t_next(&trav)) { g_warning("%d events", g_list_length(data->events)); for(GList *ptr = data->events; ptr; ptr=ptr->next) { kryEvent *line = (kryEvent *) ptr->data; if(line->GetText() != NULL) g_warning("[%p][%ld - %ld] line: %s", line, line->GetStart(), line->GetEnd(), line->GetText()); } } } /* * Static wrapper for the BeforeModifyEvent method. * Invoked before a change is applied to an event in this script. */ void kryScript::BeforeModifyEventW(kryObject *obj, void *param, kryScript *script) { kryEventDetailed *event = (kryEventDetailed *) obj; script->BeforeModifyEvent(event); } /* * Invoked before a change is applied to an event in the script. */ void kryScript::BeforeModifyEvent(kryEventDetailed *event) { this->InvokeSignal(SIGNAL_EVENT_REMOVED, event); } /* * Static wrapper for the AfterModifyEvent method. * Invoked after a change has been applied to an event in this script. */ void kryScript::AfterModifyEventW(kryObject *obj, void *param, kryScript *script) { kryEventDetailed *event = (kryEventDetailed *) obj; script->AfterModifyEvent(event); } /* * Invoked after a change has been applied to an event in the list. */ void kryScript::AfterModifyEvent(kryEventDetailed *event) { this->InvokeSignal(SIGNAL_EVENT_ADDED, event); } /* * Static wrapper for the BeforeModifyName method. * Invoked before a change is applied to the name of an event in this script. */ void kryScript::BeforeModifyNameW(kryObject *obj, void *data, kryScript *script) { kryEventDetailed *event = (kryEventDetailed *) obj; script->BeforeModifyName(event); } /* * Invoked before a change is applied to the name of an event in this script. */ void kryScript::BeforeModifyName(kryEventDetailed *event) { if(event->GetName()) this->NameHashRemove(event->GetName()); } /* * Static wrapper for the AfterModifyName method. * Invoked after a change has been applied to the name of an event in this script. */ void kryScript::AfterModifyNameW(kryObject *obj, void *data, kryScript *script) { kryEventDetailed *event = (kryEventDetailed *) obj; script->AfterModifyName(event); } /* * Invoked after a change has been applied to the name of an event in this script. */ void kryScript::AfterModifyName(kryEventDetailed *event) { this->NameHashAdd(event->GetName()); } void kryScript::SetEncoding(enum file_encoding encoding) { this->m_encoding = encoding; } enum file_encoding kryScript::GetEncoding() { return this->m_encoding; } kryScript *kryScript::Copy() { kryScript *copy = new kryScript(); if(this->GetFilename()) copy->SetFilename(this->GetFilename()); if(this->GetSuggestedFilename()) copy->SetSuggestedFilename(this->GetSuggestedFilename()); kryListIterator iter_name; this->GetPropertyNameIterator(&iter_name); while(char *prop = iter_name.GetNext()) copy->SetProperty(kry_strdup(prop), kry_strdup(this->GetProperty(prop))); this->GetStyleNameIterator(&iter_name); while(char *style = iter_name.GetNext()) copy->AddStyle(this->GetStyle(style)->Copy()); kryListIterator iter_event; this->GetEventIterator(&iter_event); while(kryEventDetailed *event = iter_event.GetNext()) copy->AddEvent(event->Copy()); return copy; }