scrolling menu ( c code ) - c

I want to create a menu with max 10 line window, and display in this window all items from a xml file ( containing more than 10 items), by scrolling up/down line in window to view all content of file.
Questions: How make this possible in my API (like in ncurses scroll menu):
"If the sub window given for a window is not big enough to show all the items, then the menu will be scrollable. When you are on the last item in the present list, if you send REQ_DOWN_ITEM, it gets translated into REQ_SCR_DLINE and the menu scrolls by one item. You can manually give REQ_SCR_ operations to do scrolling. Let's see how it can be done."
Part of my code for this feature:
static void menu( commands_t *cmd )
{
/* this display only first 10 line */
int i;
char string[ 128 ];
for( i = 0; i < 10; i++ ) {
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i ) );
menu_list_set_text( cmd->menu, i, string );
}
}
Currently, this function can display only the first 10 items from list.
A
B
C
D
E
-----------------
Line 01 | F |
Line 02 | G |
Line 03 | H |
Line 04 | I |
Line 05 | J |
Line 06 | K |
Line 07 | L |
Line 08 | M |
Line 09 | N |
Line 10 | O |
-----------------
P
Q
R
S
T
.......
Z
For this I try to create API capable to set text in menu line from list, move up/down cursor in menu but I don't now how to make to move lines up or down.
typedef struct list_info_s list_info_t;
struct list_info_s
{
int position;
char name[ 50 ];
list_info_t *next;
list_info_t *prev;
};
const list_info_t *list_get_list( list_mgr_t *mgr, int pos)
{
const list_info_t *tmp = mgr->first;
int i;
for (i = 0; tmp && i < pos; i++)
tmp = tmp->next;
return tmp;
}
const char *list_get_name( list_mgr_t *mgr, int position )
{
const list_info_t *list = list_get_list(mgr, position);
if( (list) && (*list->name) ) {
return list->name;
} else {
return 0;
}
}
TO MOVE UP/DOWN:
void commands_handle( commands_t *cmd, int prog_cmd )
{
switch( prog_cmd ) {
case MENU_UP:
cmd->menu_position = (cmd->menu_position + cmd->menu_size - 1) % (cmd->menu_size);
menu( cmd );
break;
case MENU_DOWN:
cmd->menu_position = (cmd->menu_position + 1) % (cmd->menu_size);
menu( cmd );
break;
return;
}
}
MENU:
static void menu( commands_t *cmd )
{
/* this display only first 10 line */
int i;
char string[ 128 ];
for( i = 0; i < 10; i++ ) {
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i ) );
menu_list_set_text( cmd->menu, i, string );
}
}
Windows of menu is for max 10 items lines because xml may contain a large number of items.
What programming options have to display all lines from file to be visible on menu by presing UP/DOWN button?
This is actual API for menu:
#define MENU_MAX 10
struct menu_s
{
char *name;
char text[ MENU_MAX ][ 128 ];
char arguments[ MENU_MAX ][ 128 ];
int commands[ MENU_MAX ];
char back_argument[ 128 ];
int back_command;
int numlines;
int cursor;
int defaultcursor;
};
menu_t *menu_new( const char *name )
{
menu_t *menu = malloc( sizeof( menu_t ) );
if( !menu ) {
return 0;
}
menu->numlines = 0;
menu->cursor = 0;
menu->defaultcursor = 0;
menu->name = strdup( name );
if( !menu->name ) {
free( menu );
return 0;
}
return menu;
}
void menu_delete( menu_t *menu )
{
free( menu->name );
free( menu );
}
void menu_reset_num_lines( menu_t *menu )
{
menu->numlines = 0;
}
void menu_set_text( menu_t *menu, int line, const char *text )
{
snprintf( menu->text[ line ], sizeof( menu->text[ 0 ] ), "%s", text );
if( line >= menu->numlines ) menu->numlines = line + 1;
}
void menu_set_enter_command( menu_t *menu, int line, int command,
const char *argument )
{
menu->commands[ line ] = command;
snprintf( menu->argum#define MENU_MAX 10
struct menu_s
{
char *name;
char text[ MENU_MAX ][ 128 ];
char arguments[ MENU_MAX ][ 128 ];
int commands[ MENU_MAX ];
char back_argument[ 128 ];
int back_command;
int numlines;
int cursor;
int defaultcursor;
};
menu_t *menu_new( const char *name )
{
menu_t *menu = malloc( sizeof( menu_t ) );
if( !menu ) {
return 0;
}
menu->numlines = 0;
menu->cursor = 0;
menu->defaultcursor = 0;
menu->name = strdup( name );
if( !menu->name ) {
free( menu );
return 0;
}
return menu;
}
void menu_delete( menu_t *menu )
{
free( menu->name );
free( menu );
}
void menu_reset_num_lines( menu_t *menu )
{
menu->numlines = 0;
}
void menu_set_text( menu_t *menu, int line, const char *text )
{
snprintf( menu->text[ line ], sizeof( menu->text[ 0 ] ), "%s", text );
if( line >= menu->numlines ) menu->numlines = line + 1;
}
void menu_set_enter_command( menu_t *menu, int line, int command,
const char *argument )
{
menu->commands[ line ] = command;
snprintf( menu->arguments[ line ], sizeof( menu->arguments[ 0 ] ),
"%s", argument );
}
void menu_set_back_command( menu_t *menu, int command,
const char *argument )
{
menu->back_command = command;
snprintf( menu->back_argument, sizeof( menu->back_argument ),
"%s", argument );
}
void menu_set_cursor( menu_t *menu, int cursor )
{
menu->cursor = cursor;
}
const char *menu_get_name( menu_t *menu )
{
return menu->name;
}
int menu_get_num_lines( menu_t *menu )
{
return menu->numlines;
}
const char *menu_get_text( menu_t *menu, int line )
{
return menu->text[ line ];
}
int menu_get_enter_command( menu_t *menu, int line )
{
return menu->commands[ line ];
}
const char *menu_get_enter_argument( menu_t *menu, int line )
{
return menu->arguments[ line ];
}
int menu_get_back_command( menu_t *menu )
{
return menu->back_command;
}
const char *menu_get_back_argument( menu_t *menu )
{
return menu->back_argument;
}
int menu_get_cursor( menu_t *menu )
{
return menu->cursor;
}
int menu_get_default_cursor( menu_t *menu )
{
return menu->defaultcursor;
}
I tried a ugly solution:
static void menu( commands_t *cmd )
{
int i;
for( i = 0; i < 10; i++ ) {
char string[ 128 ];
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i )
menu_list_set_text( cmd->menu, i, string );
int scroll;
for( scroll = 0; scroll < list_size; scroll++ ) {
if( cmd->curmenupos == 10 + scroll ) {
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i + scroll + 1 )
menu_list_set_text( cmd->menu, i, string );
}
}
}
}
where "cmd->curmenupos" is command to get menu line position and
"list_size" is number of items from xml

You can simplify your solution somewhat by using an array based implementation of a ring buffer and then only print your menu from the head/tail at the top left corner. Then scrolling simply becomes advancing the head/tail and overwrite the line that is disappearing with the new one. Using an array based implementation should be ok, as your line numbers is constant anyway.
Depending on how slow the IO is, your software may use less IO resources and hence improve performance, by some simple caching of the old lines. Keep in mind not to prematurely optimize This could be done by letting the ring buffer have more entries than lines on your screen, and then only overwrite when scrolling past a certain point.

Related

removing duplicated names for array in c

this is what i have to make: c. It should be possible to remove an animal with a specified name. If more animals with the same name exist, it should remove all the animal with the same name.
this is my code:
void deleteAnimalByName(char *animalName, int *nrOfAnimals, ANIMAL *animalArray)
{
for(int i = 0; i < *nrOfAnimals; i ++)
{
if(strcmp((animalArray + i)->Name, animalName) == 0)
{
for(int j = i; j < *nrOfAnimals - 1; j++)
{
animalArray[j] = animalArray[j + 1];
}
(*nrOfAnimals)--;
}
}
}
the outcome after tyring to delete the animals with the same name:
Animals in shelter: 1
Name: ted
Species: Parrot
Age: 1
only one gets deleted, the other one stays. what could cause this?
For starters the function should be declared at least like
size_t deleteAnimalByName( ANIMAL *animalArray, size_t nrOfAnimals, const char *animalName );
And the function can be defined like
size_t deleteAnimalByName( ANIMAL *animalArray, size_t nrOfAnimals, const char *animalName )
{
size_t n = 0;
for ( size_t i = 0; i < nrOfAnimals; i++ )
{
if ( strcmp( animalArray[i].Name, animalName ) != 0 )
{
if ( n != i ) animalArray[n] = animalArray[i];
++n;
}
}
return n;
}
As for your approach then it at least is inefficient because you move all elements of the array one position left after finding an element that need to be deleted.
Here is a demonstrative program
#include <stdio.h>
#include <string.h>
typedef struct ANIMAL
{
char *Name;
} ANIMAL;
size_t deleteAnimalByName( ANIMAL *animalArray, size_t nrOfAnimals, const char *animalName )
{
size_t n = 0;
for ( size_t i = 0; i < nrOfAnimals; i++ )
{
if ( strcmp( animalArray[i].Name, animalName ) != 0 )
{
if ( n != i ) animalArray[n] = animalArray[i];
++n;
}
}
return n;
}
int main(void)
{
ANIMAL animalArray[] =
{
{ "hare" }, { "hare" }, { "fox" }, { "hare" }
};
size_t nrOfAnimals = sizeof( animalArray ) / sizeof( *animalArray );
nrOfAnimals = deleteAnimalByName( animalArray, nrOfAnimals, "hare" );
for ( size_t i = 0; i < nrOfAnimals; i++ )
{
printf( "%s ", animalArray[i].Name );
}
putchar( '\n' );
return 0;
}
The program output is
fox

Read a string and get it in (int) in C

Can you help me? I have a string 23;56;36.6;run in a txt file.Then I am reading this string in order to use it for some work: I would like to take this values from a string, then compare them with some values in a code and output my result in console. I think,I should use atoi() function that make my string in numbers, for picking out , I am using strtok(). But how correctly should I record my tokens in loop while and the last token is a type of char. How can I do this work?
CODE:
void printInfo(int note)
{
int i;
FILE *out;
char str[250];
char sp[10]=";";
char *istr;
if ((out =fopen("test.txt","r"))==NULL)
printf("Error open, file\n");
else
{
for (i=0;i<note;i++)
{
fgets(str,250,out);
istr=strtok(str,sp);
while (istr != NULL)
{
printf("%d\n",atoi(istr));
istr=strtok(NULL,sp);
// I think, I need to create a variable for recording my values.
}
}
}
fclose(out);
}
I would use sscanf to convert the string to the three floats:
#include <stdio.h> // sscanf
#include <stdlib.h> // EXIT_SUCCESS
#include <string.h> // memset
int main(void) {
const char *input = "23;56;36.6;run";
int i;
float numbers[3] = {0, 0, 0};
char buf[10];
int nElementsRead;
// init buf
memset(buf, 0, sizeof(buf));
// sscanf returns the number of read elements
// or EOF on error
nElementsRead = sscanf(input, "%f;%f;%f;%9s", &numbers[0], &numbers[1], &numbers[2], buf);
if (nElementsRead == 4) {
printf("Successfully read %d elements\n", nElementsRead);
for (i = 0; i < 3; ++i) {
printf("number[%d]: %f\n", i, numbers[i]);
}
printf("Buffer is: %s\n", buf);
} else {
printf("Something went wrong!");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Another solution, using comparable records:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct MyRecord_s {
int f1;
int f2;
float f3;
char f4[10];
} MyRecord;
static int MyRecordInit( MyRecord * out, char * line ) {
if( out == NULL ) {
return 0;
}
char * f1 = strtok( line, ";" );
char * f2 = strtok( NULL, ";" );
char * f3 = strtok( NULL, ";" );
char * f4 = strtok( NULL, ";" );
if( f1 && f2 && f3 && f4 ) {
char * err = NULL;
out->f1 = strtol( f1, &err, 10 );
if( err && *err ) {
return 0;
}
out->f2 = strtol( f1, &err, 10 );
if( err && *err ) {
return 0;
}
out->f3 = strtof( f1, &err );
if( err && *err ) {
return 0;
}
strncpy( out->f4, f4, 10 );
out->f4[9] = '\0';
return 1;
}
return 0;
}
int MyRecordCmp( const MyRecord * r1, const MyRecord * r2 ) {
int diff = r1->f1 - r1->f2;
if( diff ) {
return diff;
}
diff = r1->f2 - r2->f2;
if( diff ) {
return diff;
}
float d = r1->f3 - r2->f3;
if( d > 0.000001 ) {
return +1;
}
if( d < -0.000001 ) {
return -1;
}
return strcmp( r1->f4, r2->f4 );
}
int main() {
char line1[] = "23;56;36.6;run";
char line2[] = "24;57;37.6;stop";
MyRecord r1, r2;
if( MyRecordInit( &r1, line1 ) && MyRecordInit( &r2, line2 )) {
printf( "cmp: %d\n", MyRecordCmp( &r1, &r2 ));
}
return 0;
}

C - Print job schedule using pthreads, but qPointer is null instead of pointing to front of queue

I am trying to use C to print a job schedule using pthreads. But in nextTaskInQueue(), qPointer does not cause a segmentation fault, but it is NULL instead of pointing to front of node queue. When you run the executable, be sure to add 2 on the end as the command line argument. Since my code requires input, type the following for the input: Jim (tab) A (tab) 2 (tab) 5 (enter) Mary (tab) B (tab) 2 (tab) 3 (enter) Sue (tab) A (tab) 5 (tab) 5 (enter) Mary (tab) C (tab) 6 (tab) 4 (enter). Here is my source code below.
#include<pthread.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
typedef struct node
{
char user[5];
char process;
int arrival;
int duration;
struct node *next;
}node;
typedef struct Q
{
node *R,*F;
}Q;
struct display
{
char user[5];
int timeLastCalculated;
struct display *next;
} *frontDisplay, *tempDisplay, *rearDisplay;
pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;
Q q;
void initialise(Q *);
int empty(Q *);
void enqueue(Q *, char[5], char, int, int);
void* jobDisplay();
struct node* nextTaskInQueue();
void addToSummary(char[], int);
void summaryDisplay();
int main( int argc, char *argv[] )
{
//sysconf(_SC_NPROCESSORS_ONLN);
int n;
if( argc == 2 )
{
if ( strcmp(argv[1],"2") == 0 )
{
n = atoi(argv[1]);
}
else
{
printf("The argument supplied is not 2.\n");
return 0;
}
}
else if( argc > 2 )
{
printf("Too many arguments supplied.\n");
return 0;
}
else
{
printf("One argument expected.\n");
return 0;
}
char user[5], process;
int arrivalInput, durationInput;
initialise(&q);
printf("\n\tUser\tProcess\tArrival\tDuration\n");
int i = 1;
while ( i < 5 )
{
printf("\t");
if ( scanf("%s\t%c\t%d\t%d", user, &process, &arrivalInput, &durationInput) < 4 )
{
printf("\nThe arguments supplied are incorrect. Try again.\n");
return 0;
}
enqueue(&q, user, process, arrivalInput, durationInput);
i++;
}
printf("\nThis would result in:\n\tTime\tJob\n\t");
pthread_t threadID[n];
i = 0;
for ( i = 0; i < n; i++ )
{
pthread_create(&(threadID[i]),NULL,&jobDisplay,NULL);
}
pthread_join(threadID[0],NULL);
pthread_join(threadID[1],NULL);
pthread_mutex_destroy(&m1);
pthread_mutex_destroy(&m2);
summaryDisplay();
return 0;
}
void initialise(Q *qPointer)
{
qPointer->R=NULL;
qPointer->F=NULL;
rearDisplay=NULL;
tempDisplay=NULL;
frontDisplay=NULL;
}
void enqueue(Q *qPointer, char user[5], char process, int arrivalE, int durationE)
{
node *eP;
eP=(node*)malloc(sizeof(node));
strcpy(eP->user, user);
eP->process=process;
eP->arrival=arrivalE;
eP->duration=durationE;
eP->next=NULL;
if ( empty(qPointer) )
{
qPointer->R=eP;
qPointer->F=eP;
}
else
{
(qPointer->R)->next=eP;
qPointer->R=eP;
}
}
void* jobDisplay()
{
int myTime = 0;
struct node* placeholder = NULL;
placeholder = nextTaskInQueue();
while ( placeholder != NULL )
{
if ( myTime < placeholder->arrival )
{
sleep( placeholder->arrival - myTime );
myTime = placeholder->arrival;
}
printf("%d\t%c\n", myTime, placeholder->process);
sleep(placeholder->duration);
myTime += placeholder->duration;
addToSummary(placeholder->user, myTime);
placeholder = nextTaskInQueue();
}
printf("%d\tCPU %d IDLE\n", myTime, pthread_self());
return NULL;
}
struct node* nextTaskInQueue()
{
struct node* placeholder = NULL;
pthread_mutex_lock(&m1);
struct Q* qPointer=(struct Q *)malloc(1*sizeof(struct Q));
if ( qPointer->F != NULL )
{
placeholder = qPointer->F;
qPointer->F = placeholder->next;
}
pthread_mutex_unlock(&m1);
return placeholder;
}
void addToSummary(char name[], int timeLeft)
{
pthread_mutex_lock(&m2);
if ( rearDisplay == NULL )
{
rearDisplay = (struct display *)malloc(1*sizeof(struct display));
rearDisplay->next = NULL;
strcpy(rearDisplay->user, name);
rearDisplay->timeLastCalculated = timeLeft;
frontDisplay = rearDisplay;
}
else
{
tempDisplay = frontDisplay;
while ( tempDisplay != NULL )
{
if ( strcmp(tempDisplay->user, name) == 0 )
{
tempDisplay->timeLastCalculated = timeLeft;
break;
}
tempDisplay = tempDisplay->next;
}
if ( tempDisplay == NULL )
{
tempDisplay = (struct display *)malloc(1*sizeof(struct display));
rearDisplay->next = tempDisplay;
strcpy(tempDisplay->user, name);
tempDisplay->timeLastCalculated = timeLeft;
tempDisplay->next = NULL;
rearDisplay = tempDisplay;
}
}
pthread_mutex_unlock(&m2);
}
void summaryDisplay()
{
printf("\nSummary\n");
while ( frontDisplay != NULL )
{
printf("%s\t%d\n", frontDisplay->user, frontDisplay->timeLastCalculated);
tempDisplay = frontDisplay->next;
free(frontDisplay);
frontDisplay = tempDisplay;
}
}
int empty(Q *qPointer)
{
if ( qPointer->R==NULL )
return 1;
return 0;
}

C - Appending to string - Possible memory errors

I am trying to a create a function that keeps on appending a string to a char variable. However, some times it works and other times it doesn't. I am wondering where the bug is?
char *final_output = NULL;
void add_string(const char *);
int main(void) {
add_string("Hello world\n");
add_string("This is my new function!\n");
/* Let's print */
while (final_output && *final_output) {
printf("%c", *final_output);
*final_output++;
}
}
void add_string(const char *text) {
if (final_output == NULL) {
final_output = malloc(strlen(text) + 1);
}
else {
final_output = (char *) realloc(final_output, strlen(final_output) + strlen(text) + 2);
}
strncat(final_output, text, strlen(text));
}
The problem is in function add_string. You do not append the allocated or copied array with the terminating zero after statements
final_output = malloc(strlen(text) + 1);
and
strncat(final_output, text, strlen(text));
Rewrite the function the following way
void add_string( const char *s )
{
if ( final_output == NULL )
{
final_output = malloc( strlen( s ) + 1 );
final_output[0] = '\0';
}
else
{
final_output = realloc( final_output, strlen( final_output ) + strlen( s ) + 1 );
}
strcat( final_output, s );
}

How to use a RichEdit control like a console with the Win32 API?

I have a RichEdit control in my simple application that I wish to simulate a console-like display with. I want to be able to have a buffer of x number of lines (say, 300) and whenever a line is added, I would like to also remove the oldest (top) line if the new line exceeded the threshold x. I would also like it to automatically scroll to the bottom to display the newest line when added.
I've been using SetWindowText with some success, however it occurs to me that there is likely a more efficient method of appending text to the end and removing text from the beginning without having to replace all of it every time. Is this true, and, if so, how might I go about it?
Also, how might I make it automatically scroll to the bottom of the window when new text is added?
This is using the Win32 API from C, and I'm not using the MFC version of RichEdit (just using the vanilla Win32 API on XP and Vista).
To add text, you set the selection to the end of the text (EM_SETSEL), then replace the selection with the new text (EM_REPLACESEL).
To scroll to the bottom, you can send it a WM_VSCROLL with SB_BOTTOM.
I send you some methods of sample class cConsole:
class cConsole {
private:
//-------------------
int lines;
int max_lines; // Init it with your choise ( 300 )
//-------------------
char* buf;
int buf_size;
//-------------------
int CheckMemory( int size );
void NewLine( int new_lines );
void InternalPrint( char* msg, int size );
public:
HWND hWin;
void Print( char* msg ); // Add text data through this methods
void Print( char* msg, int size );
cConsole( );
~cConsole( );
};
int cConsole::CheckMemory( int size ) {
int rv = 1;
if( size + 16 >= buf_size ) {
int new_buf_size = size + 1024;
char* new_buf = ( char* )realloc( buf, new_buf_size );
if( new_buf != NULL ) {
buf = new_buf;
buf_size = new_buf_size;
} else {
rv = 0;
}
}
return rv;
}
void cConsole::NewLine( int new_lines ) {
int rem_lines = ( new_lines + lines + 1 ) - max_lines;
if( rem_lines <= 0 ) {
lines += new_lines;
} else {
int sel = SendMessage( hWin, EM_LINEINDEX, rem_lines, 0 );
SendMessage( hWin, EM_SETSEL, 0, (LPARAM)sel );
SendMessage( hWin, EM_REPLACESEL, FALSE, (LPARAM)"" );
SendMessage( hWin, WM_VSCROLL, SB_BOTTOM, NULL );
lines = max_lines - 1;
}
}
void cConsole::Print( char* msg ) { InternalPrint( msg, -1 ); }
void cConsole::Print( char* msg, int size ) { if( size < 0 ) size = 0; InternalPrint( msg, size ); }
void cConsole::InternalPrint( char* msg, int size ) {
int s, t = 0;
int new_lines = 0;
char* tb;
// msg only mode
if( size == -1 ) size = 0x7fffffff;
if( msg != NULL && size && CheckMemory( t ) ) {
for( s = 0; msg[ s ] && ( s < size ); s++ ) {
if( msg[ s ] == '\r' ) continue;
if( !CheckMemory( t ) ) break;
if( msg[ s ] == '\n' ) {
++new_lines;
buf[ t++ ] = '\r';
}
buf[ t++ ] = msg[ s ];
}
buf[ t ] = '\0';
}
if( t && msg != NULL ) {
tb = buf;
} else {
++new_lines;
tb = "\r\n";
}
SendMessage( hWin, EM_SETSEL, (WPARAM)-2, (LPARAM)-1 );
SendMessage( hWin, EM_REPLACESEL, FALSE, (LPARAM)tb );
SendMessage( hWin, WM_VSCROLL, SB_BOTTOM, NULL );
if( new_lines ) NewLine( new_lines );
}
Built your own class and check this!

Resources