8e68e223a4
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27400 a1c6a512-1295-4272-9138-f99709370657
529 lines
16 KiB
C
529 lines
16 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006 Jonathan Gordon
|
|
*
|
|
* 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
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "plugin.h"
|
|
#include "lib/playback_control.h"
|
|
|
|
#define MAX_LINE_LEN 2048
|
|
PLUGIN_HEADER
|
|
|
|
static unsigned char *buffer;
|
|
static size_t buffer_size;
|
|
static size_t char_count = 0;
|
|
static int line_count = 0;
|
|
static int last_action_line = 0;
|
|
static int last_char_index = 0;
|
|
static bool audio_buf = false;
|
|
|
|
static char temp_line[MAX_LINE_LEN];
|
|
static char copy_buffer[MAX_LINE_LEN];
|
|
static char filename[MAX_PATH];
|
|
static char eol[3];
|
|
static bool newfile;
|
|
|
|
#define ACTION_INSERT 0
|
|
#define ACTION_GET 1
|
|
#define ACTION_REMOVE 2
|
|
#define ACTION_UPDATE 3
|
|
#define ACTION_CONCAT 4
|
|
|
|
static char* _do_action(int action, char* str, int line);
|
|
#ifndef HAVE_ADJUSTABLE_CPU_FREQ
|
|
#define do_action _do_action
|
|
#else
|
|
static char* do_action(int action, char* str, int line)
|
|
{
|
|
char *r;
|
|
rb->cpu_boost(1);
|
|
r = _do_action(action,str,line);
|
|
rb->cpu_boost(0);
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
static char* _do_action(int action, char* str, int line)
|
|
{
|
|
int len, lennew;
|
|
int i=0,c=0;
|
|
if (line>=last_action_line)
|
|
{
|
|
i = last_action_line;
|
|
c = last_char_index;
|
|
}
|
|
while (i<line && i<line_count)
|
|
{
|
|
c += rb->strlen(&buffer[c])+1;
|
|
i++;
|
|
}
|
|
switch (action)
|
|
{
|
|
case ACTION_INSERT:
|
|
len = rb->strlen(str)+1;
|
|
if ( char_count+ len > buffer_size )
|
|
return NULL;
|
|
rb->memmove(&buffer[c+len],&buffer[c],char_count-c);
|
|
rb->strcpy(&buffer[c],str);
|
|
char_count += len;
|
|
line_count++;
|
|
break;
|
|
case ACTION_GET:
|
|
if (line > line_count)
|
|
return &buffer[0];
|
|
break;
|
|
case ACTION_REMOVE:
|
|
if (line > line_count)
|
|
return NULL;
|
|
len = rb->strlen(&buffer[c])+1;
|
|
rb->memmove(&buffer[c],&buffer[c+len],char_count-c-len);
|
|
char_count -= len;
|
|
line_count--;
|
|
break;
|
|
case ACTION_UPDATE:
|
|
if (line > line_count)
|
|
return NULL;
|
|
len = rb->strlen(&buffer[c])+1;
|
|
lennew = rb->strlen(str)+1;
|
|
if ( char_count+ lennew-len > buffer_size )
|
|
return NULL;
|
|
rb->memmove(&buffer[c+lennew],&buffer[c+len],char_count-c-len);
|
|
rb->strcpy(&buffer[c],str);
|
|
char_count += lennew-len;
|
|
break;
|
|
case ACTION_CONCAT:
|
|
if (line > line_count)
|
|
return NULL;
|
|
rb->memmove(&buffer[c-1],&buffer[c],char_count-c);
|
|
char_count--;
|
|
line_count--;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
last_action_line = i;
|
|
last_char_index = c;
|
|
return &buffer[c];
|
|
}
|
|
static const char* list_get_name_cb(int selected_item, void* data,
|
|
char* buf, size_t buf_len)
|
|
{
|
|
(void)data;
|
|
char *b = do_action(ACTION_GET, 0, selected_item);
|
|
/* strlcpy(dst, src, siz) returns strlen(src) */
|
|
if (rb->strlcpy(buf, b, buf_len) >= buf_len)
|
|
{
|
|
rb->strcpy(&buf[buf_len-10], " ...");
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static void get_eol_string(char* fn)
|
|
{
|
|
int fd;
|
|
char t;
|
|
|
|
/* assume LF first */
|
|
rb->strcpy(eol,"\n");
|
|
|
|
if (!fn || !fn[0])
|
|
return;
|
|
fd = rb->open(fn,O_RDONLY);
|
|
if (fd<0)
|
|
return;
|
|
|
|
while (1)
|
|
{
|
|
if (!rb->read(fd,&t,1) || t == '\n')
|
|
{
|
|
break;
|
|
}
|
|
if (t == '\r')
|
|
{
|
|
if (rb->read(fd,&t,1) && t=='\n')
|
|
rb->strcpy(eol,"\r\n");
|
|
else
|
|
rb->strcpy(eol,"\r");
|
|
break;
|
|
}
|
|
}
|
|
rb->close(fd);
|
|
return;
|
|
}
|
|
|
|
static bool save_changes(int overwrite)
|
|
{
|
|
int fd;
|
|
int i;
|
|
|
|
if (newfile || !overwrite)
|
|
{
|
|
if(rb->kbd_input(filename,MAX_PATH) < 0)
|
|
{
|
|
newfile = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC, 0666);
|
|
if (fd < 0)
|
|
{
|
|
newfile = true;
|
|
rb->splash(HZ*2, "Changes NOT saved");
|
|
return false;
|
|
}
|
|
|
|
rb->lcd_clear_display();
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(1);
|
|
#endif
|
|
for (i=0;i<line_count;i++)
|
|
{
|
|
rb->fdprintf(fd,"%s%s", do_action(ACTION_GET, 0, i), eol);
|
|
}
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(0);
|
|
#endif
|
|
rb->close(fd);
|
|
|
|
if (newfile || !overwrite)
|
|
/* current directory may have changed */
|
|
rb->reload_directory();
|
|
|
|
newfile = false;
|
|
return true;
|
|
}
|
|
|
|
static void setup_lists(struct gui_synclist *lists, int sel)
|
|
{
|
|
rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
|
|
rb->gui_synclist_set_icon_callback(lists,NULL);
|
|
rb->gui_synclist_set_nb_items(lists,line_count);
|
|
rb->gui_synclist_limit_scroll(lists,true);
|
|
rb->gui_synclist_select_item(lists, sel);
|
|
rb->gui_synclist_draw(lists);
|
|
}
|
|
|
|
enum {
|
|
MENU_RET_SAVE = -1,
|
|
MENU_RET_NO_UPDATE,
|
|
MENU_RET_UPDATE,
|
|
};
|
|
static int do_item_menu(int cur_sel)
|
|
{
|
|
int ret = MENU_RET_NO_UPDATE;
|
|
MENUITEM_STRINGLIST(menu, "Line Options", NULL,
|
|
"Cut/Delete", "Copy",
|
|
"Insert Above", "Insert Below",
|
|
"Concat To Above",
|
|
"Save", "Playback Control");
|
|
|
|
switch (rb->do_menu(&menu, NULL, NULL, false))
|
|
{
|
|
case 0: /* cut */
|
|
rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
|
|
MAX_LINE_LEN);
|
|
do_action(ACTION_REMOVE, 0, cur_sel);
|
|
ret = MENU_RET_UPDATE;
|
|
break;
|
|
case 1: /* copy */
|
|
rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
|
|
MAX_LINE_LEN);
|
|
ret = MENU_RET_NO_UPDATE;
|
|
break;
|
|
case 2: /* insert above */
|
|
if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
|
|
{
|
|
do_action(ACTION_INSERT,copy_buffer,cur_sel);
|
|
copy_buffer[0]='\0';
|
|
ret = MENU_RET_UPDATE;
|
|
}
|
|
break;
|
|
case 3: /* insert below */
|
|
if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
|
|
{
|
|
do_action(ACTION_INSERT,copy_buffer,cur_sel+1);
|
|
copy_buffer[0]='\0';
|
|
ret = MENU_RET_UPDATE;
|
|
}
|
|
break;
|
|
case 4: /* cat to above */
|
|
if (cur_sel>0)
|
|
{
|
|
do_action(ACTION_CONCAT,0,cur_sel);
|
|
ret = MENU_RET_UPDATE;
|
|
}
|
|
break;
|
|
case 5: /* save */
|
|
ret = MENU_RET_SAVE;
|
|
break;
|
|
case 6: /* playback menu */
|
|
if (!audio_buf)
|
|
playback_control(NULL);
|
|
else
|
|
rb->splash(HZ, "Cannot restart playback");
|
|
ret = MENU_RET_NO_UPDATE;
|
|
break;
|
|
default:
|
|
ret = MENU_RET_NO_UPDATE;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
/* in misc.h but no need to polute the api */
|
|
#define toupper(c) (((c >= 'a') && (c <= 'z'))?c+'A':c)
|
|
#define isxdigit(c) ((c>='a' && c<= 'f') || (c>='A' && c<= 'F') \
|
|
|| (c>='0' && c<= '9'))
|
|
#define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
|
|
(toupper(c)) - 'A' + 10)
|
|
static int my_hex_to_rgb(const char* hex, int* color)
|
|
{ int ok = 1;
|
|
int i;
|
|
int red, green, blue;
|
|
|
|
if (rb->strlen(hex) == 6) {
|
|
for (i=0; i < 6; i++ ) {
|
|
if (!isxdigit(hex[i])) {
|
|
ok=0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
|
|
green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
|
|
blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
|
|
*color = LCD_RGBPACK(red,green,blue);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#endif /* HAVE_LCD_COLOR */
|
|
|
|
/* this is the plugin entry point */
|
|
enum plugin_status plugin_start(const void* parameter)
|
|
{
|
|
int fd;
|
|
|
|
struct gui_synclist lists;
|
|
bool exit = false;
|
|
int button;
|
|
bool changed = false;
|
|
int cur_sel=0;
|
|
#ifdef HAVE_LCD_COLOR
|
|
bool edit_colors_file = false;
|
|
#endif
|
|
|
|
copy_buffer[0]='\0';
|
|
|
|
#if LCD_DEPTH > 1
|
|
rb->lcd_set_backdrop(NULL);
|
|
#endif
|
|
buffer = rb->plugin_get_buffer(&buffer_size);
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(1);
|
|
#endif
|
|
if (parameter)
|
|
{
|
|
#ifdef HAVE_LCD_COLOR
|
|
char *c = NULL;
|
|
#endif
|
|
rb->strlcpy(filename, (char*)parameter, MAX_PATH);
|
|
get_eol_string(filename);
|
|
fd = rb->open(filename,O_RDONLY);
|
|
if (fd<0)
|
|
{
|
|
rb->splashf(HZ*2, "Couldnt open file: %s", filename);
|
|
return PLUGIN_ERROR;
|
|
}
|
|
#ifdef HAVE_LCD_COLOR
|
|
c = rb->strrchr(filename, '.');
|
|
if (c && !rb->strcmp(c, ".colours"))
|
|
edit_colors_file = true;
|
|
#endif
|
|
if (buffer_size <= (size_t)rb->filesize(fd) + 0x400)
|
|
{
|
|
buffer = rb->plugin_get_audio_buffer(&buffer_size);
|
|
audio_buf = true;
|
|
}
|
|
/* read in the file */
|
|
while (rb->read_line(fd,temp_line,MAX_LINE_LEN) > 0)
|
|
{
|
|
if (!do_action(ACTION_INSERT,temp_line,line_count))
|
|
{
|
|
rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter);
|
|
rb->close(fd);
|
|
return PLUGIN_ERROR;
|
|
}
|
|
}
|
|
rb->close(fd);
|
|
newfile = false;
|
|
}
|
|
else
|
|
{
|
|
rb->strcpy(filename,"/");
|
|
rb->strcpy(eol,"\n");
|
|
newfile = true;
|
|
}
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(0);
|
|
#endif
|
|
/* now dump it in the list */
|
|
setup_lists(&lists,0);
|
|
rb->lcd_update();
|
|
while (!exit)
|
|
{
|
|
rb->gui_synclist_draw(&lists);
|
|
cur_sel = rb->gui_synclist_get_sel_pos(&lists);
|
|
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
|
|
if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
|
|
continue;
|
|
switch (button)
|
|
{
|
|
case ACTION_STD_OK:
|
|
{
|
|
if (line_count)
|
|
rb->strlcpy(temp_line, do_action(ACTION_GET, 0, cur_sel),
|
|
MAX_LINE_LEN);
|
|
#ifdef HAVE_LCD_COLOR
|
|
if (edit_colors_file && line_count)
|
|
{
|
|
char *name = temp_line, *value = NULL;
|
|
char extension[MAX_LINE_LEN];
|
|
int color, old_color;
|
|
bool temp_changed = false;
|
|
rb->settings_parseline(temp_line, &name, &value);
|
|
if (line_count)
|
|
{
|
|
MENUITEM_STRINGLIST(menu, "Edit What?", NULL,
|
|
"Extension", "Colour");
|
|
rb->strcpy(extension, name);
|
|
if (value)
|
|
my_hex_to_rgb(value, &color);
|
|
else
|
|
color = 0;
|
|
|
|
switch (rb->do_menu(&menu, NULL, NULL, false))
|
|
{
|
|
case 0:
|
|
temp_changed = !rb->kbd_input(extension,MAX_LINE_LEN);
|
|
break;
|
|
case 1:
|
|
old_color = color;
|
|
rb->set_color(rb->screens[SCREEN_MAIN], name, &color, -1);
|
|
temp_changed = (value == NULL) || (color != old_color);
|
|
break;
|
|
}
|
|
|
|
if (temp_changed)
|
|
{
|
|
rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X",
|
|
extension, RGB_UNPACK_RED(color),
|
|
RGB_UNPACK_GREEN(color),
|
|
RGB_UNPACK_BLUE(color));
|
|
do_action(ACTION_UPDATE, temp_line, cur_sel);
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (!rb->kbd_input(temp_line,MAX_LINE_LEN))
|
|
{
|
|
if (line_count)
|
|
do_action(ACTION_UPDATE,temp_line,cur_sel);
|
|
else
|
|
do_action(ACTION_INSERT,temp_line,cur_sel);
|
|
changed = true;
|
|
}
|
|
}
|
|
break;
|
|
case ACTION_STD_CONTEXT:
|
|
if (!line_count) break;
|
|
rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
|
|
MAX_LINE_LEN);
|
|
do_action(ACTION_REMOVE, 0, cur_sel);
|
|
changed = true;
|
|
break;
|
|
case ACTION_STD_MENU:
|
|
{
|
|
/* do the item menu */
|
|
switch (do_item_menu(cur_sel))
|
|
{
|
|
case MENU_RET_SAVE:
|
|
if(save_changes(1))
|
|
changed = false;
|
|
break;
|
|
case MENU_RET_UPDATE:
|
|
changed = true;
|
|
break;
|
|
case MENU_RET_NO_UPDATE:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case ACTION_STD_CANCEL:
|
|
if (changed)
|
|
{
|
|
MENUITEM_STRINGLIST(menu, "Do What?", NULL,
|
|
"Return",
|
|
"Playback Control", "Save Changes",
|
|
"Save As...", "Save and Exit",
|
|
"Ignore Changes and Exit");
|
|
switch (rb->do_menu(&menu, NULL, NULL, false))
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
if (!audio_buf)
|
|
playback_control(NULL);
|
|
else
|
|
rb->splash(HZ, "Cannot restart playback");
|
|
break;
|
|
case 2: //save to disk
|
|
if(save_changes(1))
|
|
changed = 0;
|
|
break;
|
|
case 3:
|
|
if(save_changes(0))
|
|
changed = 0;
|
|
break;
|
|
case 4:
|
|
if(save_changes(1))
|
|
exit=1;
|
|
break;
|
|
case 5:
|
|
exit=1;
|
|
break;
|
|
}
|
|
}
|
|
else exit=1;
|
|
break;
|
|
}
|
|
rb->gui_synclist_set_nb_items(&lists,line_count);
|
|
if(line_count > 0 && line_count <= cur_sel)
|
|
rb->gui_synclist_select_item(&lists,line_count-1);
|
|
}
|
|
return PLUGIN_OK;
|
|
}
|