📄 subtitle.c
字号:
/*** Sinek (Media Player)** Copyright (c) 2001-2002 Gurer Ozen**** This code is free software; you can redistribute it and/or** modify it under the terms of the GNU General Public License.**** subtitles*/#include "common.h"#include <zlib.h>enum{ COL_START, COL_END, COL_SUBTITLE, N_COLS};typedef int (loader_t)(gzFile *fd);static GtkWidget *combo, *delay, *list_view, *text;static GtkListStore *list_store;static subtitle_t *current = NULL, *subtitles = NULL, *last = NULL;static int frame_per_second = 25;static int frame_format = 0;static float delay_time = 0;static char buffer[1024];static loader_t *find_loader(gzFile *fd);static void load_tree(void);int subtitle_load(const char *filename){ gzFile *fd; loader_t *loader; int ret; fd = gzopen(filename, "r"); if(!fd) { warning(_("Cannot open subtitle file:\n%s"), filename); return 0; } frame_format = 0; loader = find_loader(fd); if(loader) { ret = loader(fd); } else { ret = 0; warning(_("Unknown subtitle format!")); } gzclose(fd); if(ret && list_view) load_tree(); return ret;}subtitle_t *subtitle_find(unsigned long msec){ if(frame_format) msec = (msec / 100) * frame_per_second; if(msec < (delay_time * 100)) return(NULL); msec = msec - (delay_time * 100); if(current == NULL) current = subtitles; while(current) { if(msec >= current->start && msec < current->end) { return current; } else if(msec < current->start) { if(current->prev && current->prev->end < msec) return NULL; current = current->prev; } else if(msec >= current->end) { if(current->next && current->next->start > msec) return NULL; current = current->next; } } return NULL;}void subtitle_delete(void){ current = NULL; subtitles = NULL; last = NULL;}static subtitle_t *append(unsigned long start, unsigned long end){ subtitle_t *sub; sub = malloc(sizeof(subtitle_t)); memset(sub, 0, sizeof(subtitle_t)); sub->start = start; sub->end = end; sub->prev = last; if(!subtitles) subtitles = sub; if(last) last->next = sub; last = sub; if(sub->prev) { if(sub->prev->end >= sub->start) printf("error in %ld - %ld\n", sub->start, sub->end); } return sub;}static void append_text(subtitle_t *sub, char *line){ if(sub->lines < 4) { sub->text[sub->lines] = g_strdup(line); sub->lines++; }}static int parse_microdvd(gzFile *fd){ subtitle_t *sub; char buf2[1024]; unsigned long start, end; char *t, *s; frame_format = 1; while(1) { if(!gzgets(fd, buffer, 1000)) break; if(sscanf(buffer, "{%ld}{%ld}%[^\r\n]", &start, &end, buf2) < 3) continue; sub = append(start, end); t = buf2; while(1) { s = strchr(t, '|'); if(s) { *s = '\0'; append_text(sub, t); t = s + 1; } else { append_text(sub, t); break; } } } return 1;}static int parse_subrip(gzFile *fd){ subtitle_t *sub; unsigned long start, end; char *t, *s; int a1, a2, a3, a4, b1, b2, b3, b4; while(1) { if(!gzgets(fd, buffer, 1000)) break; if(sscanf(buffer, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) continue; start = a1*360000 + a2*6000 + a3*100 + a4; end = b1*360000 + b2*6000 + b3*100 + b4; if(!gzgets(fd, buffer, 1000)) break; strtok(buffer, "\r\n"); sub = append(start, end); t = buffer; while(1) { s = strstr(t, "[br]"); if(s) { *s = '\0'; append_text(sub, t); t = s + 4; } else { append_text(sub, t); break; } } } return 1;}static int parse_subrip2(gzFile *fd){ subtitle_t *sub; unsigned long start, end; char *t, *s; int a1, a2, a3, a4, b1, b2, b3, b4; while(1) { if(!gzgets(fd, buffer, 1000)) break; if(sscanf(buffer, "%d:%d:%d,%d,%d:%d:%d,%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) continue; start = a1*360000 + a2*6000 + a3*100 + a4; end = b1*360000 + b2*6000 + b3*100 + b4; if(!gzgets(fd, buffer, 1000)) break; strtok(buffer, "\r\n"); sub = append(start, end); t = buffer; while(1) { s = strstr(t, "[br]"); if(s) { *s = '\0'; append_text(sub, t); t = s + 4; } else { append_text(sub, t); break; } } } return 1;}static int parse_srt(gzFile *fd){ subtitle_t *sub; unsigned long start, end; int a1, a2, a3, a4, b1, b2, b3, b4; while(1) { if(!gzgets(fd, buffer, 1000)) break; if(sscanf(buffer, "%d:%d:%d,%d --> %d:%d:%d,%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) continue; start = a1*360000 + a2*6000 + a3*100 + (a4 / 10); end = b1*360000 + b2*6000 + b3*100 + (b4 / 10); sub = append(start, end); while(1) { if(!gzgets(fd, buffer, 1000)) break; if(buffer[0] == '\0' || buffer[0] == '\r' || buffer[0] == '\n') break; strtok(buffer, "\r\n"); append_text(sub, buffer); } } return 1;}static loader_t *find_loader(gzFile *fd){ loader_t *loader = NULL; int i, j; for(i = 0; i < 50; i++) { if(!gzgets(fd, buffer, 1000)) break; if(sscanf(buffer, "{%d}{%d}", &j, &j) == 2) { loader = parse_microdvd; break; } if(sscanf(buffer, "%d:%d:%d.%d,%d:%d:%d.%d", &j, &j, &j, &j, &j, &j, &j, &j) == 8) { loader = parse_subrip; break; } if(sscanf(buffer, "%d:%d:%d,%d,%d:%d:%d,%d", &j, &j, &j, &j, &j, &j, &j, &j) == 8) { loader = parse_subrip2; break; } if(sscanf(buffer, "%d:%d:%d,%d --> %d:%d:%d,%d", &j, &j, &j, &j, &j, &j, &j, &j) == 8) { loader = parse_srt; break; } } gzrewind(fd); return loader;}static void cb_frame_rate(GtkWidget *w, gpointer data){ const gchar *str; int val; str = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry)); if(str) { val = atoi(str); if(val > 0 && val < 80) frame_per_second = val; }}void sub_save(char *filename){ FILE *fd; subtitle_t *tmp; unsigned long start, end; int a, b, c, d, e, f; fd = fopen(filename, "w"); if(fd) { tmp = subtitles; while(tmp) { if(frame_format) { start = tmp->start / frame_per_second; end = tmp->end / frame_per_second; } else { start = tmp->start / 100; end = tmp->end / 100; } if(start >= delay_time) { start = start - delay_time; end = end - delay_time; } a = start / 3600; b = (start % 3600) / 60; c = start % 60; d = end / 3600; e = (end % 3600) / 60; f = end % 60; fprintf(fd, "%02d:%02d:%02d.00,%02d:%02d:%02d.00\n", a, b, c, d, e, f); a = 0; while(a < tmp->lines) { fprintf(fd, "%s", tmp->text[a]); a++; if(a < tmp->lines) fprintf(fd, "[br]"); } fprintf(fd, "\n\n"); tmp = tmp->next; } fclose(fd); }}static void cb_save(GtkWidget *w, gpointer data){ select_file(sub_save, _("Save subtitles as..."));}static void cb_delay(GtkWidget *w, gpointer data){ const gchar *str; str = gtk_entry_get_text(GTK_ENTRY(delay)); if(str) { delay_time = atof(str); }}static void cb_select_line(GtkTreeSelection *sel, gpointer data){ GtkTreeModel *model; GtkTreeIter iter; GtkTextIter iter2; GtkTextBuffer *tb; subtitle_t *sub; int i = 1; if(gtk_tree_selection_get_selected(sel, &model, &iter)) { gtk_tree_model_get(model, &iter, COL_START, &sub, -1); tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); gtk_text_buffer_set_text(tb, sub->text[0], -1); gtk_text_buffer_get_end_iter(tb, &iter2); while(i < sub->lines) { gtk_text_buffer_insert(tb, &iter2, "\n", 1); gtk_text_buffer_insert(tb, &iter2, sub->text[i] , -1); i++; } }}static void cb_render_start(GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data){ subtitle_t *tmp; gchar *buf; unsigned int sec, a, b, c; gtk_tree_model_get(model, iter, COL_START, &tmp, -1); if(data) sec = (tmp->end / 100); else sec = (tmp->start / 100); a = sec / 3600; b = (sec % 3600) / 60; c = sec % 60; buf = g_strdup_printf("%02d:%02d:%02d", a, b, c); g_object_set(cell, "text", buf, NULL);}static void load_tree(void){ GtkTreeIter iter; subtitle_t *tmp = subtitles; gtk_list_store_clear(list_store); while(tmp) { gtk_list_store_append(list_store, &iter); gtk_list_store_set(list_store, &iter, COL_START, tmp, COL_END, "00:00:00", COL_SUBTITLE, tmp->text[0], -1); tmp = tmp->next; }}int subtitle_build(GtkWidget **win){ GtkWidget *w, *vb, *vb2, *hb, *hb2, *b, *sw; GtkCellRenderer *cell; GtkTreeViewColumn *col; GList *items = NULL; w = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(w), _("Sinek Subtitle Editor")); gtk_window_set_wmclass(GTK_WINDOW(w), "subtitles", "sinek"); vb = gtk_vbox_new(FALSE, 5); gtk_widget_show(vb); gtk_container_add(GTK_CONTAINER(w), vb); gtk_container_set_border_width(GTK_CONTAINER(vb), 5); hb = gtk_hbox_new(FALSE, 5); gtk_widget_show(hb); gtk_box_pack_start(GTK_BOX(vb), hb, TRUE, TRUE, 0); /* subtitle list */ vb2 = gtk_vbox_new(FALSE, 5); gtk_widget_show(vb2); gtk_box_pack_start(GTK_BOX(hb), vb2, TRUE, TRUE, 0); sw = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(sw); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_box_pack_start(GTK_BOX(vb2), sw, TRUE, TRUE, 3); list_store = gtk_list_store_new(N_COLS, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING); list_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store)); cell = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes(_("Start"), cell, NULL); gtk_tree_view_column_set_cell_data_func(col, cell, cb_render_start, NULL, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), col); cell = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes(_("End"), cell, NULL); gtk_tree_view_column_set_cell_data_func(col, cell, cb_render_start, (gpointer)1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), col); cell = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes(_("Subtitle"), cell, "text", COL_SUBTITLE, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), col); g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view))), "changed", G_CALLBACK(cb_select_line), NULL); if(subtitles) load_tree(); gtk_widget_show(list_view); gtk_container_add(GTK_CONTAINER(sw), list_view); /* edit area */ sw = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(sw); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_box_pack_start(GTK_BOX(vb2), sw, FALSE, TRUE, 3); text = gtk_text_view_new(); gtk_widget_show(text); gtk_container_add(GTK_CONTAINER(sw), text); gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); /* frame rate & delay adjustments */ vb2 = gtk_vbox_new(FALSE, 5); gtk_widget_show(vb2); gtk_box_pack_start(GTK_BOX(hb), vb2, FALSE, FALSE, 0); b = gtk_label_new(_("Frame rate:")); gtk_widget_show(b); gtk_box_pack_start(GTK_BOX(vb2), b, FALSE, FALSE, 0); hb2 = gtk_hbox_new(FALSE, 5); gtk_widget_show(hb2); gtk_box_pack_start(GTK_BOX(vb2), hb2, FALSE, FALSE, 0); combo = gtk_combo_new(); gtk_widget_show(combo); gtk_box_pack_start(GTK_BOX(hb2), combo, TRUE, TRUE, 0); items = g_list_append(items, (gpointer) "15"); items = g_list_append(items, (gpointer) "25"); items = g_list_append(items, (gpointer) "29.7"); gtk_combo_set_popdown_strings(GTK_COMBO(combo), items); g_list_free(items); b = GTK_COMBO(combo)->entry; gtk_entry_set_max_length(GTK_ENTRY(b), 6); gtk_entry_set_width_chars(GTK_ENTRY(b), 6); gtk_entry_set_text(GTK_ENTRY(b), "25"); b = gtk_label_new(_("fps")); gtk_widget_show(b); gtk_box_pack_start(GTK_BOX(hb2), b, FALSE, FALSE, 0); b = gtk_button_new_with_label(_("Change")); gtk_widget_show(b); gtk_box_pack_start(GTK_BOX(vb2), b, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(cb_frame_rate), NULL); b = gtk_label_new(_("Delay:")); gtk_widget_show(b); gtk_box_pack_start(GTK_BOX(vb2), b, FALSE, FALSE, 0); hb2 = gtk_hbox_new(FALSE, 5); gtk_widget_show(hb2); gtk_box_pack_start(GTK_BOX(vb2), hb2, FALSE, FALSE, 0); delay = gtk_entry_new(); gtk_widget_show(delay); gtk_box_pack_start(GTK_BOX(hb2), delay, TRUE, TRUE, 0); gtk_entry_set_max_length(GTK_ENTRY(delay), 6); gtk_entry_set_width_chars(GTK_ENTRY(delay), 6); gtk_entry_set_text(GTK_ENTRY(delay), "0.0"); b = gtk_label_new(_("sec")); gtk_widget_show(b); gtk_box_pack_start(GTK_BOX(hb2), b, FALSE, FALSE, 0); b = gtk_button_new_with_label(_("Change")); gtk_widget_show(b); gtk_box_pack_start(GTK_BOX(vb2), b, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(cb_delay), NULL); /* save & load buttons */ b = gtk_hseparator_new(); gtk_widget_show(b); gtk_box_pack_start(GTK_BOX(vb), b, FALSE, FALSE, 0); hb2 = gtk_hbutton_box_new(); gtk_widget_show(hb2); gtk_box_pack_start(GTK_BOX(vb), hb2, FALSE, FALSE, 0); gtk_button_box_set_layout(GTK_BUTTON_BOX(hb2), GTK_BUTTONBOX_END); gtk_container_set_border_width(GTK_CONTAINER(hb2), 5); gtk_box_set_spacing(GTK_BOX(hb2), 5);/* b = gtk_button_new_with_label(_("Open...")); gtk_widget_show(b); gtk_container_add(GTK_CONTAINER(hb2), b); b = gtk_button_new_with_label(_("Save")); gtk_widget_show(b); gtk_container_add(GTK_CONTAINER(hb2), b);*/ b = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS); gtk_widget_show(b); gtk_container_add(GTK_CONTAINER(hb2), b); g_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(cb_save), NULL); b = gtk_button_new_from_stock(GTK_STOCK_CLOSE); gtk_widget_show(b); gtk_container_add(GTK_CONTAINER(hb2), b); g_signal_connect_swapped(G_OBJECT(b), "clicked", G_CALLBACK(gtk_widget_hide), GTK_OBJECT(w)); *win = w; return WM_NORMAL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -