1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2003 Linus Nielsen Feltzing
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
31 #include "string-extra.h"
33 #include "filefuncs.h"
46 static int curr_preset
= -1;
48 extern int curr_freq
; /* from radio.c.. naughty but meh */
49 extern int radio_mode
;
50 int snap_freq_to_grid(int freq
);
51 void remember_frequency(void);
52 void talk_freq(int freq
, bool enqueue
);
54 #define MAX_PRESETS 64
55 static bool presets_loaded
= false;
56 static bool presets_changed
= false;
57 static struct fmstation presets
[MAX_PRESETS
];
59 static char filepreset
[MAX_PATH
]; /* preset filename variable */
61 static int num_presets
= 0; /* The number of presets in the preset list */
63 bool yesno_pop(const char* text
); /* radio.c */
65 int radio_current_preset(void)
69 int radio_preset_count(void)
73 const struct fmstation
*radio_get_preset(int preset
)
75 return &presets
[preset
];
78 bool presets_have_changed(void)
80 return presets_changed
;
84 /* Find a matching preset to freq */
85 int preset_find(int freq
)
90 for(i
= 0;i
< MAX_PRESETS
;i
++)
92 if(freq
== presets
[i
].frequency
)
99 /* Return the closest preset encountered in the search direction with
101 static int find_closest_preset(int freq
, int direction
)
108 if (direction
== 0) /* direction == 0 isn't really used */
111 for (i
= 0; i
< num_presets
; i
++)
113 int f
= presets
[i
].frequency
;
115 return i
; /* Exact match = stop */
117 /* remember the highest and lowest presets for wraparound */
118 if (f
< presets
[lowpreset
].frequency
)
120 if (f
> presets
[highpreset
].frequency
)
123 /* find the closest preset in the given direction */
124 if (direction
> 0 && f
> freq
)
126 if (closest
< 0 || f
< presets
[closest
].frequency
)
129 else if (direction
< 0 && f
< freq
)
131 if (closest
< 0 || f
> presets
[closest
].frequency
)
138 /* no presets in the given direction */
139 /* wrap around depending on direction */
141 closest
= highpreset
;
149 void preset_next(int direction
)
154 if (curr_preset
== -1)
155 curr_preset
= find_closest_preset(curr_freq
, direction
);
157 curr_preset
= (curr_preset
+ direction
+ num_presets
) % num_presets
;
159 /* Must stay on the current grid for the region */
160 curr_freq
= snap_freq_to_grid(presets
[curr_preset
].frequency
);
162 tuner_set(RADIO_FREQUENCY
, curr_freq
);
163 remember_frequency();
166 void preset_set_current(int preset
)
168 curr_preset
= preset
;
171 /* Speak a preset by number or by spelling its name, depending on settings. */
172 void preset_talk(int preset
, bool fallback
, bool enqueue
)
174 if (global_settings
.talk_file
== 1) /* number */
175 talk_number(preset
+ 1, enqueue
);
178 if(presets
[preset
].name
[0])
179 talk_spell(presets
[preset
].name
, enqueue
);
181 talk_freq(presets
[preset
].frequency
, enqueue
);
186 void radio_save_presets(void)
191 fd
= creat(filepreset
, 0666);
194 for(i
= 0;i
< num_presets
;i
++)
196 fdprintf(fd
, "%d:%s\n", presets
[i
].frequency
, presets
[i
].name
);
200 if(!strncasecmp(FMPRESET_PATH
, filepreset
, strlen(FMPRESET_PATH
)))
201 set_file(filepreset
, global_settings
.fmr_file
, MAX_FILENAME
);
202 presets_changed
= false;
206 splash(HZ
, ID2P(LANG_FM_PRESET_SAVE_FAILED
));
210 void radio_load_presets(char *filename
)
220 memset(presets
, 0, sizeof(presets
));
223 /* No Preset in configuration. */
224 if(filename
[0] == '\0')
226 filepreset
[0] = '\0';
229 /* Temporary preset, loaded until player shuts down. */
230 else if(filename
[0] == '/')
231 strlcpy(filepreset
, filename
, sizeof(filepreset
));
232 /* Preset from default directory. */
234 snprintf(filepreset
, sizeof(filepreset
), "%s/%s.fmr",
235 FMPRESET_PATH
, filename
);
237 fd
= open_utf8(filepreset
, O_RDONLY
);
240 while(!done
&& num_presets
< MAX_PRESETS
)
242 rc
= read_line(fd
, buf
, 128);
245 if(settings_parseline(buf
, &freq
, &name
))
248 if(f
) /* For backwards compatibility */
250 struct fmstation
* const fms
= &presets
[num_presets
];
252 strlcpy(fms
->name
, name
, MAX_FMPRESET_LEN
+1);
262 else /* invalid file name? */
263 filepreset
[0] = '\0';
265 presets_loaded
= num_presets
> 0;
266 presets_changed
= false;
269 const char* radio_get_preset_name(int preset
)
271 if (preset
< num_presets
)
272 return presets
[preset
].name
;
276 int handle_radio_add_preset(void)
278 char buf
[MAX_FMPRESET_LEN
+ 1];
280 if(num_presets
< MAX_PRESETS
)
284 if (!kbd_input(buf
, MAX_FMPRESET_LEN
+ 1))
286 struct fmstation
* const fms
= &presets
[num_presets
];
287 strcpy(fms
->name
, buf
);
288 fms
->frequency
= curr_freq
;
290 presets_changed
= true;
291 presets_loaded
= num_presets
> 0;
297 splash(HZ
, ID2P(LANG_FM_NO_FREE_PRESETS
));
302 /* needed to know which preset we are edit/delete-ing */
303 static int selected_preset
= -1;
304 static int radio_edit_preset(void)
306 char buf
[MAX_FMPRESET_LEN
+ 1];
310 struct fmstation
* const fms
= &presets
[selected_preset
];
312 strcpy(buf
, fms
->name
);
314 if (!kbd_input(buf
, MAX_FMPRESET_LEN
+ 1))
316 strcpy(fms
->name
, buf
);
317 presets_changed
= true;
324 static int radio_delete_preset(void)
328 struct fmstation
* const fms
= &presets
[selected_preset
];
330 if (selected_preset
>= --num_presets
)
331 selected_preset
= num_presets
- 1;
333 memmove(fms
, fms
+ 1, (uintptr_t)(fms
+ num_presets
) -
336 if (curr_preset
>= num_presets
)
340 /* Don't ask to save when all presets are deleted. */
341 presets_changed
= num_presets
> 0;
343 if (!presets_changed
)
345 /* The preset list will be cleared, switch to Scan Mode. */
346 radio_mode
= RADIO_SCAN_MODE
;
348 presets_loaded
= false;
354 int preset_list_load(void)
356 char selected
[MAX_PATH
];
357 struct browse_context browse
;
358 snprintf(selected
, sizeof(selected
), "%s.%s", global_settings
.fmr_file
, "fmr");
359 browse_context_init(&browse
, SHOW_FMR
, 0,
360 str(LANG_FM_PRESET_LOAD
), NOICON
,
361 FMPRESET_PATH
, selected
);
362 return !rockbox_browse(&browse
);
365 int preset_list_save(void)
369 bool bad_file_name
= true;
371 if(!dir_exists(FMPRESET_PATH
)) /* Check if there is preset folder */
372 mkdir(FMPRESET_PATH
);
374 create_numbered_filename(filepreset
, FMPRESET_PATH
, "preset",
375 ".fmr", 2 IF_CNFN_NUM_(, NULL
));
379 if(!kbd_input(filepreset
, sizeof(filepreset
)))
381 /* check the name: max MAX_FILENAME (20) chars */
385 p1
= strrchr(filepreset
, '/');
387 while((p1
) && (*p2
) && (*p2
!= '.'))
389 len
= (int)(p2
-p1
) - 1;
390 if((!p1
) || (len
> MAX_FILENAME
) || (len
== 0))
392 /* no slash, too long or too short */
393 splash(HZ
, ID2P(LANG_INVALID_FILENAME
));
397 /* add correct extension (easier to always write)
398 at this point, p2 points to 0 or the extension dot */
400 strcat(filepreset
,".fmr");
401 bad_file_name
= false;
402 radio_save_presets();
413 splash(HZ
, ID2P(LANG_FM_NO_PRESETS
));
418 int preset_list_clear(void)
420 /* Clear all the preset entries */
421 memset(presets
, 0, sizeof (presets
));
424 presets_loaded
= false;
425 /* The preset list will be cleared switch to Scan Mode. */
426 radio_mode
= RADIO_SCAN_MODE
;
428 presets_changed
= false; /* Don't ask to save when clearing the list. */
433 MENUITEM_FUNCTION(radio_edit_preset_item
, MENU_FUNC_CHECK_RETVAL
,
434 ID2P(LANG_FM_EDIT_PRESET
),
435 radio_edit_preset
, NULL
, NULL
, Icon_NOICON
);
436 MENUITEM_FUNCTION(radio_delete_preset_item
, MENU_FUNC_CHECK_RETVAL
,
437 ID2P(LANG_FM_DELETE_PRESET
),
438 radio_delete_preset
, NULL
, NULL
, Icon_NOICON
);
439 static int radio_preset_callback(int action
,
440 const struct menu_item_ex
*this_item
)
442 if (action
== ACTION_STD_OK
)
443 action
= ACTION_EXIT_AFTER_THIS_MENUITEM
;
447 MAKE_MENU(handle_radio_preset_menu
, ID2P(LANG_PRESET
),
448 radio_preset_callback
, Icon_NOICON
, &radio_edit_preset_item
,
449 &radio_delete_preset_item
);
450 /* present a list of preset stations */
451 static const char* presets_get_name(int selected_item
, void *data
,
452 char *buffer
, size_t buffer_len
)
455 struct fmstation
*p
= &presets
[selected_item
];
458 int freq
= p
->frequency
/ 10000;
459 int frac
= freq
% 100;
461 snprintf(buffer
, buffer_len
,
462 str(LANG_FM_DEFAULT_PRESET_NAME
), freq
, frac
);
466 static int presets_speak_name(int selected_item
, void * data
)
469 preset_talk(selected_item
, true, false);
473 int handle_radio_presets(void)
475 struct gui_synclist lists
;
477 int action
= ACTION_NONE
;
478 #ifdef HAVE_BUTTONBAR
479 struct gui_buttonbar buttonbar
;
482 if(presets_loaded
== false)
485 #ifdef HAVE_BUTTONBAR
486 gui_buttonbar_init(&buttonbar
);
487 gui_buttonbar_set_display(&buttonbar
, &(screens
[SCREEN_MAIN
]) );
488 gui_buttonbar_set(&buttonbar
, str(LANG_FM_BUTTONBAR_ADD
),
489 str(LANG_FM_BUTTONBAR_EXIT
),
490 str(LANG_FM_BUTTONBAR_ACTION
));
491 gui_buttonbar_draw(&buttonbar
);
493 gui_synclist_init(&lists
, presets_get_name
, NULL
, false, 1, NULL
);
494 gui_synclist_set_title(&lists
, str(LANG_PRESET
), NOICON
);
495 gui_synclist_set_icon_callback(&lists
, NULL
);
496 if(global_settings
.talk_file
)
497 gui_synclist_set_voice_callback(&lists
, presets_speak_name
);
498 gui_synclist_set_nb_items(&lists
, num_presets
);
499 gui_synclist_select_item(&lists
, curr_preset
<0 ? 0 : curr_preset
);
500 gui_synclist_speak_item(&lists
);
504 gui_synclist_draw(&lists
);
505 list_do_action(CONTEXT_STD
, TIMEOUT_BLOCK
,
506 &lists
, &action
, LIST_WRAP_UNLESS_HELD
);
509 case ACTION_STD_MENU
:
510 if (handle_radio_add_preset())
512 gui_synclist_set_nb_items(&lists
, num_presets
);
513 gui_synclist_select_item(&lists
, num_presets
- 1);
516 case ACTION_STD_CANCEL
:
520 curr_preset
= gui_synclist_get_sel_pos(&lists
);
521 curr_freq
= presets
[curr_preset
].frequency
;
523 remember_frequency();
527 case ACTION_STD_CONTEXT
:
528 selected_preset
= gui_synclist_get_sel_pos(&lists
);
529 do_menu(&handle_radio_preset_menu
, NULL
, NULL
, false);
530 gui_synclist_set_nb_items(&lists
, num_presets
);
531 gui_synclist_select_item(&lists
, selected_preset
);
532 gui_synclist_speak_item(&lists
);
535 if(default_event_handler(action
) == SYS_USB_CONNECTED
)
543 int presets_scan(void *viewports
)
547 struct viewport
*vp
= (struct viewport
*)viewports
;
550 screens
[i
].set_viewport(vp
?&vp
[i
]:NULL
);
551 if(num_presets
> 0) /* Do that to avoid 2 questions. */
552 do_scan
= yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS
));
556 const struct fm_region_data
* const fmr
=
557 &fm_region_data
[global_settings
.fm_region
];
559 curr_freq
= fmr
->freq_min
;
561 memset(presets
, 0, sizeof(presets
));
563 tuner_set(RADIO_MUTE
, 1);
565 while(curr_freq
<= fmr
->freq_max
)
568 if(num_presets
>= MAX_PRESETS
|| action_userabort(TIMEOUT_NOBLOCK
))
571 freq
= curr_freq
/ 10000;
575 splashf(0, str(LANG_FM_SCANNING
), freq
, frac
);
577 if(tuner_set(RADIO_SCAN_FREQUENCY
, curr_freq
))
580 presets
[num_presets
].name
[0] = '\0';
581 presets
[num_presets
].frequency
= curr_freq
;
585 curr_freq
+= fmr
->freq_step
;
588 if (get_radio_status() == FMRADIO_PLAYING
)
589 tuner_set(RADIO_MUTE
, 0);
591 presets_changed
= true;
595 screens
[i
].clear_viewport();
596 screens
[i
].update_viewport();
601 curr_freq
= presets
[0].frequency
;
602 radio_mode
= RADIO_PRESET_MODE
;
603 presets_loaded
= true;
608 /* Wrap it to beginning or we'll be past end of band */
609 presets_loaded
= false;
617 void presets_save(void)
619 if(filepreset
[0] == '\0')
622 radio_save_presets();
625 #ifdef HAVE_LCD_BITMAP
626 static inline void draw_vertical_line_mark(struct screen
* screen
,
629 screen
->set_drawmode(DRMODE_COMPLEMENT
);
630 screen
->vline(x
, y
, y
+h
-1);
633 /* draw the preset markers for a track of length "tracklen",
634 between (x,y) and (x+w,y) */
635 void presets_draw_markers(struct screen
*screen
,
636 int x
, int y
, int w
, int h
)
639 const struct fm_region_data
*region_data
=
640 &(fm_region_data
[global_settings
.fm_region
]);
641 int len
= region_data
->freq_max
- region_data
->freq_min
;
642 for (i
=0; i
< radio_preset_count(); i
++)
644 int freq
= radio_get_preset(i
)->frequency
;
645 int diff
= freq
- region_data
->freq_min
;
646 xi
= x
+ (w
* diff
)/len
;
647 draw_vertical_line_mark(screen
, xi
, y
, h
);