Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Начало нового приложения для работы с коллекцией компакт-дисков




Первая секция программного кода просто связана с объявлениями переменных и функций, которые вы будете применять позже, и инициализацией некоторых структур данных.

1. Включите в программу все приведенные заголовочные файлы и несколько глобальных переменных:

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <curses.h>

#define MAX_STRING 80 /* Самый длинный допустимый ответ */

#define MAX_ENTRY 1024 /* Самый длинный допустимый элемент БД */

#define MESSAGE_LINE 6 /* В этой строке разные сообщения */

#define ERROR LINE 22 /* Строка для вывода ошибок */

#define Q_LINE 20 /* Строка для вопросов */

#define PROMPT_LINE 18 /* Строка для вывода приглашения */

2. Теперь вам нужны глобальные переменные. Переменная

current_cdis

применяется для хранения названия текущего компакт-диска, с которым вы работаете в данный момент. Она инициализируется так, что первый символ равен

NULL

, чтобы показать, что компакт-диск не выбран. Символ завершения

\0

, строго говоря, не обязателен, но он гарантирует инициализацию переменной, что само по себе хорошая вещь. Переменная

current_cat

применяется для записи номера текущего компакт-диска в каталоге:

static char current_cd[MAX_STRING] = "\0";

static char current_cat[MAX_STRING];

3. Теперь объявите имена файлов. Для простоты в этой версии имена файлов фиксированные, как и имя временного файла.

Это может вызвать проблемы, если программа выполняется двумя пользователями в одном и том же каталоге. Лучше получать имена файлов базы данных как аргументы программы или из переменных окружения. Нам также потребуется улучшенный метод генерации уникального имени временного файла, для чего мы могли бы использовать функцию

tmpnam

из стандарта POSIX. Мы обратимся к решению многих из этих проблем в главе 8, когда применим СУРБД MySQL для хранения данных.

const char *title_file = "title.cdb";

const char *tracks_file = "tracks.cdb";

const char *temp_file = "cdb.tmp";

4. И наконец, прототипы функций:

void clear_all_screen(void);

void get_return(void);

int get_confirm(void);

int getchoice(char *greet, char *choices[]);

void draw_menu(char *options[], int highlight,

int start_row, int start_col);

void insert_title(char *cdtitle);

void get_string(char *string);

void add_record(void);

void count_cds(void);

void find_cd(void);

void list_tracks(void);

void remove_tracks(void);

void remove_cd(void);

void update_cd(void);

5. Прежде чем рассматривать их реализацию, введем некоторые структуры (на самом деле массив пунктов меню) для хранения меню. Когда выбирается пункт меню, возвращается первый символ выбранного пункта. Например, если это пункт меню add new CD (добавить новый CD), при его выборе будет возвращен символ

а

. Когда компакт-диск выбран, будет отображаться расширенное меню.

char *main_menu[] = {

"add new CD",

"find CD",

"count CDs and tracks in the catalog",

"quit",

0,

};

char *extended_menu[] = {

"add new CD",

"find CD",

"count CDs and tracks in the catalog",

"list tracks on current CD";

"remove current CD",

"update track information",

"quit",

0,

};

На этом инициализация закончена. Теперь можно переходить к функциям программы, но сначала необходимо составить общее представление о взаимосвязях всех 16 функций. Функции разделены на три программных секции:

□ отображение меню;

□ добавление компакт-дисков в базу данных;

□ извлечение и отображение данных компакт-диска.

Визуальное представление дано на рис. 6.9.

Рис. 6.9

Взгляд на функцию main

Функция

main

позволяет выбирать пункты меню, пока не выбран вариант выхода из меню (quit). Далее приведен соответствующий код.

int main() {

int choice;

initscr();

do {

choice = getchoice("Options:", current_cd[0]? extended_menu: main_menu);

switch (choice) {

case 'q':

break;

case 'a':

add_record();

break;

case 'c':

count_cds();

break;

case 'f':

find_cd();

break;

case 'l':

list_tracks();

break;

case 'r':

remove_cd();

break;

case 'u':

update_cd();

break;

}

} while (choice!= 'q');

endwin();

exit(EXIT_SUCCESS);

}

Теперь давайте подробно рассмотрим функции, связанные с тремя секциями программы.

Формирование меню

В этой секции рассматриваются три функции, относящиеся к пользовательскому интерфейсу программы.

1. Функция

getchoice

, вызываемая из функции

main

, — это основная функция данной секции. В функцию

getchoice

передается приглашение

greet

и указатель

choices

на базовое или расширенное меню (в зависимости от того, выбран ли компакт-диск). Вы также увидите, как

main_menu

или

extended_menu

передаются как параметры в описанную ранее функцию

main

.

int get_choice(char *greet, char* choises[]) {

static int selected_row = 0;

int max_row = 0;

int start_screenrow = MESSAGE_LINE, start_screencol = 10;

char **option;

int selected;

int key = 0;

option = choices;

while (*option) {

max_row++;

option++;

}

if (selected_row >= max_row)

selected_row = 0;

clear_all_screen();

mvprintw(start_screenrow - 2, start_screencol, greet);

keypad(stdscr, TRUE);

cbreak();

noecho();

key = 0;

while (key!= 'q' && key!= KEY_ENTER && key!= '\n') {

if (key == KEY_UP) {

if (selected_row == 0) selected_row = max_row - 1;

else selected_row--;

}

if (key == KEY_DOWN) {

if (selected_row == (max_row - 1)) selected_row = 0;

else selected_row++;

}

selected = *choices[selected_row];

draw_menu(choices, selected_row, start_screen_row, start_screencol);

key = getch();

}

keypad(stdscr, FALSE);

nocbreak();

echo();

if (key == 'q') selected = 'q';

return(selected);

}

2. Обратите внимание на то, как две локальные функции

clear_all_screen

и

draw_menu

вызываются внутри функции

getchoice

. Первой рассмотрим функцию

draw_menu

:

void draw_menu(char* options[], int current_highlight, int start_row, int start_col) {

int current_row = 0;

char **option_ptr;

char *txt_ptr;

option_ptr = options;

while (*option_ptr) {

if (current_row == current_highlight) attron(A_STANDOUT);

txt_ptr = options[current_row];

txt_ptr++;

mvprintw(start_row + current_row, start_col, "%s", txt_ptr);

if (current_row == current_highlight) attroff(A_STANDOUT);

current_row++;

option_ptr++;

}

mvprintw(start_row + current_row + 3, start_col,

"Move highlight then press Return ");

refresh();

}

3. Далее рассмотрим функцию

clear_all_screen

, которая, как ни странно, очищает экран и перезаписывает заголовок. Если компакт-диск выбран, отображаются его данные:

void clear all_screen() {

clear();

mvprintw(2, 20, "%s", "CD Database Application");

if (current_cd[0]) {

mvprintw(ERROR_LINE, 0, "Current CD: %s: %s\n", current_cat, current_cd);

}

refresh();

}

Управление базой данных

В этом разделе описаны функции пополнения или обновления базы данных компакт-дисков. Функции

add_record

,

update_cd

и

remove_cd

вызываются из функции

main

.

Добавление записей

1. Добавьте сведения о новом компакт-диске в базу данных.

void add_record {

char catalog_number[MAX_STRING];

char cd_title[MAX_STRING];

char cd_type[MAX_STRING];

char cd_artist[MAX_STRING];

char cd_entry[MAX_STRING];

int screenrow = MESSAGE_LINE;

int screencol = 10;

clear_all_screen();

mvprintw(screenrow, screencol, "Enter new CD details");

screenrow += 2;

mvprintw(screenrow, screencol, "Catalog Number: ");

get_string(catalog_number);

screenrow++;

mvprintw(screenrow, screencol, " CD Title: ");

get_string(cd_title);

screenrow++;

mvprintw(screenrow, screencol, " CD Type: ");

get_string(cd_type);

screenrow++;

mvprintw(screenrow, screencol, " Artist: ");

get_string(cd_artist);

screenrow++;

mvprintw(PROMPT_LINE-2, 5, "About to add this new entry:");

sprintf(cd_entry, "%s, %s, %s, %s",

catalog_number, cd_title, cd_type, cd_artist);

mvprintw(PROMPT_LINE, 5, "%s", cd_entry);

refresh();

move(PROMPT_LINE, 0);

if (get_confirm()) {

insert_title(cd_entry);

strcpy(current_cd, cd_title);

strcpy(current_cat, catalog_number);

}

}

2. Функция

get_string

приглашает к вводу и считывает строку из текущей позиции экрана. Она также удаляет завершающую новую пустую строку:

void get_string(char* string) {

int len;

wgetnstr(stdscr, string, MAX_STRING);

len = strlen(string);

if (len > 0 && string[len - 1] == '\n') string[len - 1] = '\0';

}

3. Функция

get_confirm

запрашивает и считывает пользовательское подтверждение. Она читает введенную пользователем строку и проверяет, первый символ —

Y

или

у

. Если она обнаруживает другой символ, то не дает подтверждения.

int get_confirm() {

int confirmed = 0;

char first_char;

mvprintw(Q_LINE, 5, "Are you sure? ");

clrtoeol();

refresh();

cbreak();

first_char = getch();

if (first_char == 'Y' || first_char == 'y') {

confirmed = 1;

}

nocbreak();

if (!confirmed) {

mvprintw(Q_LINE, 1, " Cancelled");

clrtoeol();

refresh();

sleep(1);

}

return confirmed;

}

4. Последней рассмотрим функцию

insert_title

. Она вставляет в базу данных компакт-дисков заголовок, добавляя строку с заголовком в конец файла заголовков:

void insert_title(char* cdtitle) {

FILE *fp = fopen(title_file, "a");

if (!fp) {

mvprintw(ERROR_LINE, 0, "cannot open CD titles database");

} else {

fprintf(fp, "%s\n", cdtitle);

fclose(fp);

}

}

Обновление записей

1. Продолжим рассмотрение других управляющих функций, вызываемых из функции

main

. Следующая из них — функция

update_cd

. Эта функция использует обведенное рамкой вложенное окно с прокруткой и нуждается в нескольких константах, которые объявляются как глобальные, поскольку они позже потребуются функции

list_tracks

.

#define BOXED_LINES 11

#define BOXED_ROWS 60

#define BOX_LINE_POS 8

#define BOX_ROW_POS 2

2. Функция

update_cd

позволяет пользователю заново ввести сведения о дорожках текущего компакт-диска. Удалив предыдущие записи о дорожках, она приглашает ввести новую информацию.

void update_cd() {

FILE *tracks_fp;

char track_name[MAX_STRING];

int len;

int track = 1;

int screen_line = 1;

WINDOW *box_window_ptr;

WINDOW *sub_window_ptr;

clear_all_screen();

mvprintw(PROMPT_LINE, 0, "Re-entering tracks for CD. ");

if (!get_confirm())

return;

move(PROMP_TLINE, 0);

clrtoeol();

remove_tracks();

mvprintw(MESSAGE_LINE, 0, "Enter a blank line to finish");

tracks_fp = fopen(tracks_file, "a");

Примечание

Листинг будет продолжен через минуту; мы хотим сделать краткую паузу, чтобы обратить ваше внимание на ввод данных в обрамленное окно с прокруткой. Хитрость заключается в формировании вложенного окна, рисовании рамки по его краю и создании внутри этого окна нового вложенного окна с прокруткой.

box_window_ptr = subwin(stdscr, BOXED_LINES + 2, BOXED_ROWS + 2,

BOX_LINE_POS - 1, BOX_ROW_POS - 1);

if (!box_window_ptr) return;

box(box_window_ptr, ACS_VLINE, ACS_HLINE);

sub_window_ptr = subwin(stdscr, BOXED_LINES, BOXED_ROWS,

BOX_LINE_POS, BOX_ROW_POS);

if (!sub_window_ptr) return;

scrollok(sub_window_ptr, TRUE);

werase(sub_window_ptr);

touchwin(stdscr);

do {

mvwprintw(sub_window_ptr, screen_line++, BOX_ROW_POS + 2,

"Track %d: ", track);

clrtoeol();

refresh();

wgetnstr(sub_window_ptr, track_name, MAX_STRING);

len = strlen(track_name);

if (len > 0 && track_name[len - 1] = '\n')

track_name[len - 1] = '\0';

if (*track_name)

fprintf(tracks_fp, "%s, %d, %s\n", current_cat, track, track_name);

track++;

if (screen_line > BOXED__LINES - 1) {

/* время начать прокрутку */

scroll(sub_window_ptr);

screen_line--;

}

} while (*track_name);

delwin(sub_window_ptr);

fclose(tracks_fp);

}

Удаление записей

1.

remove_cd

— последняя функция, вызываемая из функции

main

.

void remove_cd() {

FILE *titles_fp, *temp_fp;

char entry[MAX_ENTRY];

int cat_length;

if (current_cd[0] == '\0') return;

clear_all_screen();

mvprintw(PROMPT_LINE, 0, "About to remove CD %s: %s. ", current_cat, current_cd);

if (!get_confirm())

return;

cat_length = strlen(current_cat);

/* Файл заголовков копируется во временный, игнорируя данный CD */

titles_fp = fopen(title_file, "r");

temp_fp = fopen(temp_flie, "w");

while(fgets(entry, MAX_ENTRY, titles_fp)) {

/* Сравнивает номер в каталоге и копирует элемент, если не

найдено совпадение */

if (strncmp(current_cat, entry, cat_length)!= 0)

fputs(entry, temp_fp);

}

fclose(titles_fp);

fclose(temp_fp);

/* Удаляет файл заголовков и переименовывает временный файл */

unlink(title_file);

rename(temp_file, title_file);

/* Теперь делает то же самое для файла дорожек */

remove_tracks();

/* Устанавливает 'None' для текущего CD */

current_cd[0] = '\0';

}

2. Теперь вам только нужен программный код функции

remove_tracks

, удаляющей дорожки текущего компакт-диска. Она вызывается двумя функциями —

update_cd

и

remove_cd

.

void remove_tracks() {

FILE *tracks_fp, *temp_fp;

char entry[MAX_ENTRY];

int cat_length;

if (current_cd[0] == '\0') return;

cat_length = strlen(current_cat);

tracks_fp = fopen(tracks_file, "r");

if (tracks_fp == (FILE *)NULL) return;

temp_fp = fopen(temp_file, "w");

while (fgets(entry, MAX_ENTRY, tracks_fp)) {

/* Сравнивает номер в каталоге и копирует элемент, если не

найдено совпадение */

if (strncmp(current_cat, entry, cat_length)!= 0)

fputs(entry, temp_fp);

}

fclose(tracks_fp);

fclose(temp_fp);

/* Удаляет файл дорожек и переименовывает временный файл */

unlink(tracks_file);

rename(temp_file, tracks_file);

}

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...