From 2f515e26df05882b149eaccddc7a38851971df10 Mon Sep 17 00:00:00 2001 From: key Date: Fri, 17 Jun 2022 17:04:15 +0200 Subject: [PATCH] Redesigning the menu interface to be modular --- ked/ncurses/bakcup/main.cpp | 60 ++++ ked/ncurses/bakcup/main.h | 4 + ked/ncurses/bakcup/menu.h | 194 ++++++++++++ ked/ncurses/{ => bakcup}/menuBar.h | 8 +- ked/ncurses/hele.h | 194 ++++++++++++ ked/ncurses/item.h | 76 +++++ ked/ncurses/main.cpp | 83 ++--- ked/ncurses/menu | Bin 34256 -> 25888 bytes ked/ncurses/menu.h | 341 ++++++++++++++------- ked/ncurses/training/menuEx/compile_run.sh | 2 +- ked/ncurses/training/menuEx/main.c | 178 +++++------ ked/ncurses/training/menuEx/menu | Bin 17864 -> 18664 bytes 12 files changed, 901 insertions(+), 239 deletions(-) create mode 100644 ked/ncurses/bakcup/main.cpp create mode 100644 ked/ncurses/bakcup/main.h create mode 100644 ked/ncurses/bakcup/menu.h rename ked/ncurses/{ => bakcup}/menuBar.h (92%) create mode 100644 ked/ncurses/hele.h create mode 100644 ked/ncurses/item.h diff --git a/ked/ncurses/bakcup/main.cpp b/ked/ncurses/bakcup/main.cpp new file mode 100644 index 0000000..2ecadb4 --- /dev/null +++ b/ked/ncurses/bakcup/main.cpp @@ -0,0 +1,60 @@ +#include "main.h" + +#include "terminal.h" +#include "menuBar.h" + +// INFO: wgetch(win); or getch(); // getch will make a refresh for us. + +int main(int argv, char ** argc) +{ + //Variables + int xMax, yMax, xMouse, yMouse= 0; + char pressed; + + std::string menu1[4]={"New","Open","Save","Exit"}; + std::string menu2[3]={"Copy","Cut","Paste"}; + std::string menu3[5]={"Terminal","Help","Info","Update","Manual"}; + + // Second stage declarations + Menu menus[3]={ // init the menus + Menu("File", 0, 0, 'f', menu1, 4), + Menu("Edit", 1, 0, 'e', menu2, 3), + Menu("Options", 2, 0, 'o', menu3, 5), + }; + + Terminal terminal; + MenuBar menuBar = MenuBar(menus,1,5,5,3); + + xMax = terminal.getXMax(); + yMax = terminal.getYMax(); + + std::string termToPrint = "Terminal with "; + termToPrint += std::to_string(xMax); + termToPrint += "x"; + termToPrint += std::to_string(yMax); + termToPrint += " is drawn"; + + terminal.draw(1,1,termToPrint); + terminal.draw(1,3,"Press f For Files e For Edit and o for Options"); + terminal.draw(1,2,"Press K to kill the menu"); + + terminal.endDraw(); + menuBar.drawBox(); + menuBar.drawMenus(); + + while(pressed != 'k') + { + pressed = menuBar.getKey(); + menuBar.handleTrigger(pressed); + menuBar.drawMenus(); + terminal.endDraw(); + } + menuBar.~MenuBar(); + + terminal.draw(1,2,"Press any key to exit the application"); + terminal.endDraw(); + terminal.getKey(); + terminal.tearDown(); + + return 0; +} diff --git a/ked/ncurses/bakcup/main.h b/ked/ncurses/bakcup/main.h new file mode 100644 index 0000000..da83032 --- /dev/null +++ b/ked/ncurses/bakcup/main.h @@ -0,0 +1,4 @@ +#include +#include + +#define ctrl(x) (x & 0x1F) // Detection of ctrl+x presses please do not forget that in unix ctrl+c and ctrl+z are special commands. diff --git a/ked/ncurses/bakcup/menu.h b/ked/ncurses/bakcup/menu.h new file mode 100644 index 0000000..3cd32e7 --- /dev/null +++ b/ked/ncurses/bakcup/menu.h @@ -0,0 +1,194 @@ +#include "main.h" +#ifndef _MENU_H_ +#define _MENU_H_ + +class Menu +{ + public: + Menu(std::string name, int menuNo, int direction, char trigger, std::string *items, int itemCount); + ~Menu(); + void draw(); + void nextItem(); + void prevItem(); + int getSelectedItem(); + void setSelectedItem(); + void setPosX(int x); + void setPosY(int y); + int getPosX(); + int getPosY(); + void setMenuBarBegX(int x); // it must be indicated from menu bar so that we can print the box on the right place + void setMenuBarBegY(int y); // it must be indicated from menu bar so that we can print the box on the right place + void setMenuBarSizeX(int x); // it must be indicated from menu bar so that we can print the box on the right place + std::string getName(); + int getNameLenght(); + int getLongestItemLenght(); + char getTrigger(); + void drawBox(); + void drawItems(); + int getBoxBegY(); + int getBoxBegX(); + void eraseBox(); + bool isBoxCreated(); + + private: + WINDOW * menuItemsWin; + std::string name; + std::string *items; + int menuNo, currPos, direction= 0; + int posX, xSize, menuBarBegX = 0; + int posY, ySize, menuBarBegY = 0; + int menuBarSizeX = 0; + int selected_item = 0; + int itemCount = 0; + int itemLenght = 0; + char trigger; + bool boxIsCreated = false; +}; + +Menu::Menu(std::string name, int menuNo, int direction, char trigger, std::string *items, int itemCount) +{ + this->name = name; //Name of the menu + this->menuNo = menuNo; //No of the menu so that we can index it with the menuBar.handdleTrigger option. + this->direction = direction; //Direction o the menu Bar Horizontal = 0, vertical = 1 + this->trigger = trigger; //The trigger for the given menu + this->items = items; //Items present in thei menu + this->itemCount = itemCount; //Nouber of item present in this menu + + //To be sure + xSize = 0; + ySize = 0; + selected_item = -1; // No menus are selected a the beggining + boxIsCreated = false; + + // Vertical Alignment + if(direction) + { + } + else // Horizontal Alignment -> The Deffault + { + } + +} + +Menu::~Menu() +{ + werase(menuItemsWin); + wclear(menuItemsWin); + wrefresh(menuItemsWin); + boxIsCreated = false; +} + +void Menu::eraseBox() +{ + werase(menuItemsWin); + wclear(menuItemsWin); + wrefresh(menuItemsWin); + boxIsCreated = false; +} + +void Menu::setPosX(int x) +{ + this->posX = x; +} + +void Menu::setPosY(int y) +{ + this->posY = y; +} + +int Menu::getPosX() +{ + return posX; +} + +int Menu::getPosY() +{ + return posY; +} + +void Menu::setMenuBarBegX(int x) +{ + this->menuBarBegX = x; +} + +void Menu::setMenuBarBegY(int y) +{ + this->menuBarBegY = y; +} + +void Menu::setMenuBarSizeX(int x) +{ + this->menuBarSizeX = x; +} +std::string Menu::getName() +{ + return name; +} + +int Menu::getNameLenght() +{ + return name.length(); +} + +int Menu::getLongestItemLenght() +{ + return xSize; +} + +char Menu::getTrigger() +{ + return trigger; +} + +int Menu::getBoxBegX() +{ + // Vertical Alignment + if(direction) + { + return menuBarSizeX+posX+menuBarBegX-1; + } + else // Horizontal Alignment -> The Deffault + { + return menuBarSizeX+posX+menuBarBegX-1; + } + return -1; +} + +int Menu::getBoxBegY() +{ + // Vertical Alignment + if(direction) + { + return posY+menuBarBegY-1; + } + else // Horizontal Alignment -> The Deffault + { + return posY+menuBarBegY-1; + } + return -1; +} + + +bool Menu::isBoxCreated() +{ + return boxIsCreated; +} + + +void Menu::drawBox() +{ + // Creates a box with caclulated dimention xSize & ySize and user given position + /*menuItemsWin = newwin(20, 20, getBoxBegY(), getBoxBegX()); + boxIsCreated = true; + box(menuItemsWin ,0,0); //Here we can define other cahr insterad of 0,0 + mvwprintw(menuItemsWin,1,1,"PosY:%d PosX:%d",getBoxBegY(), getBoxBegX()); + wrefresh(menuItemsWin); //Refresh so that we can see it +*/ + } + +void Menu::drawItems() +{ + +} + +#endif diff --git a/ked/ncurses/menuBar.h b/ked/ncurses/bakcup/menuBar.h similarity index 92% rename from ked/ncurses/menuBar.h rename to ked/ncurses/bakcup/menuBar.h index 449c1dc..9fb0177 100644 --- a/ked/ncurses/menuBar.h +++ b/ked/ncurses/bakcup/menuBar.h @@ -41,6 +41,7 @@ MenuBar::MenuBar(Menu *menus, int direction, int xBeg, int yBeg, int menuCount) xSize = 0; ySize = 0; selected_menu = -1; // No menus are selected a the beggining + prevSelected_menu = - 1; // Deselect for the next itteration. (unless handleTrigger has a new trigger) // Vertical Alignment @@ -112,14 +113,19 @@ void MenuBar::drawMenus() wattron(menuBarWin, A_STANDOUT); mvwprintw(menuBarWin, menus[i].getPosY(),menus[i].getPosX(), menus[i].getName().c_str()); wattroff(menuBarWin, A_STANDOUT); - menus[i].drawBox(); // Draws the lesefted Menus Items + //menus[i].drawBox(); // Draws the lesefted Menus Items } else // Otherwise do not hightlight { + if(menus[i].isBoxCreated()) + { + //menus[i].eraseBox(); // Draws the lesefted Menus Items + } mvwprintw(menuBarWin, menus[i].getPosY(),menus[i].getPosX(), menus[i].getName().c_str()); } } selected_menu = - 1; // Deselect for the next itteration. (unless handleTrigger has a new trigger) + prevSelected_menu = - 1; // Deselect for the next itteration. (unless handleTrigger has a new trigger) wrefresh(menuBarWin); // so we can see it } diff --git a/ked/ncurses/hele.h b/ked/ncurses/hele.h new file mode 100644 index 0000000..3cd32e7 --- /dev/null +++ b/ked/ncurses/hele.h @@ -0,0 +1,194 @@ +#include "main.h" +#ifndef _MENU_H_ +#define _MENU_H_ + +class Menu +{ + public: + Menu(std::string name, int menuNo, int direction, char trigger, std::string *items, int itemCount); + ~Menu(); + void draw(); + void nextItem(); + void prevItem(); + int getSelectedItem(); + void setSelectedItem(); + void setPosX(int x); + void setPosY(int y); + int getPosX(); + int getPosY(); + void setMenuBarBegX(int x); // it must be indicated from menu bar so that we can print the box on the right place + void setMenuBarBegY(int y); // it must be indicated from menu bar so that we can print the box on the right place + void setMenuBarSizeX(int x); // it must be indicated from menu bar so that we can print the box on the right place + std::string getName(); + int getNameLenght(); + int getLongestItemLenght(); + char getTrigger(); + void drawBox(); + void drawItems(); + int getBoxBegY(); + int getBoxBegX(); + void eraseBox(); + bool isBoxCreated(); + + private: + WINDOW * menuItemsWin; + std::string name; + std::string *items; + int menuNo, currPos, direction= 0; + int posX, xSize, menuBarBegX = 0; + int posY, ySize, menuBarBegY = 0; + int menuBarSizeX = 0; + int selected_item = 0; + int itemCount = 0; + int itemLenght = 0; + char trigger; + bool boxIsCreated = false; +}; + +Menu::Menu(std::string name, int menuNo, int direction, char trigger, std::string *items, int itemCount) +{ + this->name = name; //Name of the menu + this->menuNo = menuNo; //No of the menu so that we can index it with the menuBar.handdleTrigger option. + this->direction = direction; //Direction o the menu Bar Horizontal = 0, vertical = 1 + this->trigger = trigger; //The trigger for the given menu + this->items = items; //Items present in thei menu + this->itemCount = itemCount; //Nouber of item present in this menu + + //To be sure + xSize = 0; + ySize = 0; + selected_item = -1; // No menus are selected a the beggining + boxIsCreated = false; + + // Vertical Alignment + if(direction) + { + } + else // Horizontal Alignment -> The Deffault + { + } + +} + +Menu::~Menu() +{ + werase(menuItemsWin); + wclear(menuItemsWin); + wrefresh(menuItemsWin); + boxIsCreated = false; +} + +void Menu::eraseBox() +{ + werase(menuItemsWin); + wclear(menuItemsWin); + wrefresh(menuItemsWin); + boxIsCreated = false; +} + +void Menu::setPosX(int x) +{ + this->posX = x; +} + +void Menu::setPosY(int y) +{ + this->posY = y; +} + +int Menu::getPosX() +{ + return posX; +} + +int Menu::getPosY() +{ + return posY; +} + +void Menu::setMenuBarBegX(int x) +{ + this->menuBarBegX = x; +} + +void Menu::setMenuBarBegY(int y) +{ + this->menuBarBegY = y; +} + +void Menu::setMenuBarSizeX(int x) +{ + this->menuBarSizeX = x; +} +std::string Menu::getName() +{ + return name; +} + +int Menu::getNameLenght() +{ + return name.length(); +} + +int Menu::getLongestItemLenght() +{ + return xSize; +} + +char Menu::getTrigger() +{ + return trigger; +} + +int Menu::getBoxBegX() +{ + // Vertical Alignment + if(direction) + { + return menuBarSizeX+posX+menuBarBegX-1; + } + else // Horizontal Alignment -> The Deffault + { + return menuBarSizeX+posX+menuBarBegX-1; + } + return -1; +} + +int Menu::getBoxBegY() +{ + // Vertical Alignment + if(direction) + { + return posY+menuBarBegY-1; + } + else // Horizontal Alignment -> The Deffault + { + return posY+menuBarBegY-1; + } + return -1; +} + + +bool Menu::isBoxCreated() +{ + return boxIsCreated; +} + + +void Menu::drawBox() +{ + // Creates a box with caclulated dimention xSize & ySize and user given position + /*menuItemsWin = newwin(20, 20, getBoxBegY(), getBoxBegX()); + boxIsCreated = true; + box(menuItemsWin ,0,0); //Here we can define other cahr insterad of 0,0 + mvwprintw(menuItemsWin,1,1,"PosY:%d PosX:%d",getBoxBegY(), getBoxBegX()); + wrefresh(menuItemsWin); //Refresh so that we can see it +*/ + } + +void Menu::drawItems() +{ + +} + +#endif diff --git a/ked/ncurses/item.h b/ked/ncurses/item.h new file mode 100644 index 0000000..2c6cbab --- /dev/null +++ b/ked/ncurses/item.h @@ -0,0 +1,76 @@ +#include "main.h" + +#ifndef _ITEM_H_ +#define _ITEM_H_ + +#define BRD_SPC 1 +class Item +{ + public: + Item(std::string name, char trigger); + ~Item(); + + //Getters + int getPosX(); + int getPosY(); + int getLength(); + char getTrigger(); + std::string getName(); + + //Setters + void setPosX(int x); + void setPosY(int y); + + private: + std::string name; // Name of the item + int xPos, yPos = 0; // Coordinates of the item + int itemLength = 0; //Lenght of the item + char trigger; // trigger to select this item +}; + +Item::Item(std::string name, char trigger) +{ + this->name = name; + this->trigger = trigger; +} + +Item::~Item() +{ + +} + +//Getters +int Item::getPosX() +{ + return xPos; +} +int Item::getPosY() +{ + return yPos; +} +int Item::getLength() +{ + return name.length(); +} + +char Item::getTrigger() +{ + return trigger; +} + +std::string Item::getName() +{ + return name; +} +//Setters +void Item::setPosX(int x) +{ + this->xPos = x; +} + +void Item::setPosY(int y) +{ + this->yPos = y; +} + +#endif diff --git a/ked/ncurses/main.cpp b/ked/ncurses/main.cpp index 902cd1b..29df403 100644 --- a/ked/ncurses/main.cpp +++ b/ked/ncurses/main.cpp @@ -1,59 +1,64 @@ #include "main.h" - #include "terminal.h" -#include "menuBar.h" +#include "menu.h" +#include -// INFO: wgetch(win); or getch(); // getch will make a refresh for us. +#define HORIZONTAL 0 +#define VERTICAL 1 +#define LINK_MAIN 1 int main(int argv, char ** argc) { //Variables - int xMax, yMax, xMouse, yMouse= 0; + int xMax, yMax = 0; char pressed; - - std::string menu1[4]={"New","Open","Save","Exit"}; - std::string menu2[3]={"Copy","Cut","Paste"}; - std::string menu3[5]={"Terminal","Help","Info","Update","Manual"}; - - // Second stage declarations - Menu menus[3]={ // init the menus - Menu("File", 0, 0, 'f', menu1, 4), - Menu("Edit", 1, 0, 'e', menu2, 3), - Menu("Options", 2, 0, 'o', menu3, 5), - }; + + Item devices[3] = { + Item("Stm32f042",'q'), + Item("Stm32g441",'w'), + Item("Raspberry",'e'), + }; - Terminal terminal; - MenuBar menuBar = MenuBar(menus,1,5,5,3); + Item mainMenu[3] = { + Item("Devices",'d'), + Item("Configrations",'c'), + Item("Help",'h'), + }; + + Terminal terminal = Terminal(); + Menu menu = Menu("Main", mainMenu, 0, 0, LINK_MAIN, HORIZONTAL, 5,5,3); + Menu menuDevices = Menu("Devices", devices, 1, 1, LINK_MAIN, VERTICAL, 5,5,3); + init_pair(2, COLOR_CYAN, COLOR_BLACK); xMax = terminal.getXMax(); yMax = terminal.getYMax(); - - std::string termToPrint = "Terminal with "; - termToPrint += std::to_string(xMax); - termToPrint += "x"; - termToPrint += std::to_string(yMax); - termToPrint += " is drawn"; - - terminal.draw(1,1,termToPrint); - terminal.draw(1,3,"Press f For Files e For Edit and o for Options"); - terminal.draw(1,2,"Press K to kill the menu"); - refresh(); - menuBar.drawBox(); - menuBar.drawMenus(); + attron(COLOR_PAIR(2)); + terminal.draw(1,yMax-2,"Press x to Exit"); + attroff(COLOR_PAIR(2)); + + terminal.endDraw(); - while(pressed != 'k') + while(pressed != 'x') { - pressed = menuBar.getKey(); - menuBar.handleTrigger(pressed); - menuBar.drawMenus(); + menu.printMenu(); + pressed = terminal.getKey(); + menu.handleTrigger(pressed); + if(menu.isActive()) + { + terminal.draw(1,yMax-4,"Main Menu Is Active"); + terminal.endDraw(); + } + else + { + terminal.draw(1,yMax-4,"Main Menu is Inactive"); + } + + terminal.endDraw(); } - menuBar.~MenuBar(); - - refresh(); - terminal.draw(1,2,"Press any key to exit the application"); - terminal.getKey(); + terminal.endDraw(); + terminal.draw(1,yMax-3,"The window should be deleted"); terminal.tearDown(); return 0; diff --git a/ked/ncurses/menu b/ked/ncurses/menu index eb095fb65b9140ab096452a6c02749dac08adf60..1aefaf87b15062771e48c91934545d2cce7506ec 100755 GIT binary patch literal 25888 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 diff --git a/ked/ncurses/menu.h b/ked/ncurses/menu.h index ca9af38..d085663 100644 --- a/ked/ncurses/menu.h +++ b/ked/ncurses/menu.h @@ -1,174 +1,305 @@ #include "main.h" +#include "item.h" + #ifndef _MENU_H_ #define _MENU_H_ +#define BRD_SPC 1 class Menu { - public: - Menu(std::string name, int menuNo, int direction, char trigger, std::string *items, int itemCount); + public: + Menu( std::string name, + Item *items, + int level, int no, int link, + int orientation, + int xBeg, int yBeg, + int itemCount); ~Menu(); - void draw(); - void nextItem(); - void prevItem(); + + //Functions + void initBox(); // Inits the box dimentions and sets it up with basic parameters + void drawBox(); + void deleteMenu(); + void startDraw(); + void endDraw(); + void update(); + void printItems(); + void selectItem(); + void findTheLongestItem(); + void printMenu(); + void handleTrigger(char ch); + bool isActive(); + + //Getters + int getSizeX(); + int getSizeY(); + int getBegX(); + int getBegY(); + int getLongestItem(); int getSelectedItem(); - void setSelectedItem(); - void setPosX(int x); - void setPosY(int y); - int getPosX(); - int getPosY(); - void setMenuBarBegX(int x); // it must be indicated from menu bar so that we can print the box on the right place - void setMenuBarBegY(int y); // it must be indicated from menu bar so that we can print the box on the right place - void setMenuBarSizeX(int x); // it must be indicated from menu bar so that we can print the box on the right place - std::string getName(); - int getNameLenght(); - int getLongestItemLenght(); - char getTrigger(); - void drawBox(); - void drawItems(); - int getBoxBegY(); - int getBoxBegX(); - void eraseBox(); - - private: - WINDOW * menuItemsWin; - std::string name; - std::string *items; - int menuNo, currPos, direction= 0; - int posX, xSize, menuBarBegX = 0; - int posY, ySize, menuBarBegY = 0; - int menuBarSizeX = 0; - int selected_item = 0; - int itemCount = 0; - int itemLenght = 0; - char trigger; + int getOrientation(); + char getKey(); + + //Setters + void setBegX(int x); + void setBegY(int y); + void setTopLine(char ch); + void setBottomLine(char ch); + void setLeftLine(char ch); + void setRightLine(char ch); + void setActive(); + void setInactive(); + + private: + WINDOW *menuWin; // window fo this menu + std::string name; // Name of the menu + Item *items; // Items plresent for this menu + int orientation = 0; // 0 = Horizontal 1 = Vertical + int level = 0; // Hierarchical level of the menu 0 is the top one + int no = 0; // To be able to track echa menu indvidualy + int xBeg, yBeg = 0; // Beginning coordinates of the menu window + int xSize, ySize = 0; // Size of the menu window + int itemCount = 0; // Number of elents in this menu + int selectedItem = 0; // Currenlty selected Item on the menu + int link = 0; //If this menu is linked to anothar one // level 0 menus can not be linked + int longestItem = 0; //Lenght Of the Longest item + int i, pos = 0; + bool activeMenu = false; }; -Menu::Menu(std::string name, int menuNo, int direction, char trigger, std::string *items, int itemCount) +Menu::Menu( std::string name, + Item *items, + int level, int no, int link, + int orientation, + int xBeg, int yBeg, + int itemCount) { - this->name = name; //Name of the menu - this->menuNo = menuNo; //No of the menu so that we can index it with the menuBar.handdleTrigger option. - this->direction = direction; //Direction o the menu Bar Horizontal = 0, vertical = 1 - this->trigger = trigger; //The trigger for the given menu - this->items = items; //Items present in thei menu - this->itemCount = itemCount; //Nouber of item present in this menu + this->name = name; + this->items = items; + this->level = level; + this->no = no; + this->link = link; + this->orientation = orientation; + this->xBeg = xBeg; + this->yBeg = yBeg; + this->itemCount = itemCount; - //To be sure - xSize = 0; - ySize = 0; - selected_item = -1; // No menus are selected a the beggining + longestItem = 0; + xSize = 0; + ySize = 0; + selectedItem = - 1; + activeMenu = false; + + initBox(); +} + +//TODO: The correct way to end or erase s window is still unclear +Menu::~Menu() +{ + deleteMenu(); + werase(menuWin); +} + +void Menu::initBox() +{ + pos = 1; // So that it doesn write over the border + + findTheLongestItem(); - // Vertical Alignment - if(direction) + // if Horizontal aligment else vertical + if(orientation) { + xSize = longestItem + 2; + + for(i = 0; i < itemCount ; i ++) + { + this->items[i].setPosY(pos); + this->items[i].setPosX(1); // So tha it write in the middle + pos ++; + } + ySize = itemCount + 2; } - else // Horizontal Alignment -> The Deffault + else { + for(i = 0; i < itemCount ; i ++) + { + items[i].setPosX(pos); + items[i].setPosY(1); + pos += this->items[i].getLength() + 1; + } + xSize = pos; + ySize = 3; } - + + // Creates a box with caclulated dimention xSize & ySize and user given position + menuWin = newwin(ySize, xSize, yBeg, xBeg); } -Menu::~Menu() +//Functions +void Menu::drawBox() { - werase(menuItemsWin); - wrefresh(menuItemsWin); + //menuWin = newwin(5, 5, 5, 5); + box(menuWin,0,0); //Here we can define other cahr insterad of 0,0 + wrefresh(menuWin); //Refresh so that we can see it } -void Menu::eraseBox() + +//TODO: The correct way to end or erase s window is still unclear +void Menu::deleteMenu() { - werase(menuItemsWin); - wrefresh(menuItemsWin); + wclear(menuWin); + endDraw(); } - -void Menu::setPosX(int x) + +void Menu::startDraw() { - this->posX = x; + } -void Menu::setPosY(int y) +void Menu::endDraw() { - this->posY = y; + wrefresh(menuWin); //Refresh so that we can see it +} + +void Menu::update() +{ + } -int Menu::getPosX() +void Menu::printItems() { - return posX; + for(i=0 ; i < itemCount; i++) + { + if(i == selectedItem) + { + wattron(menuWin, A_STANDOUT); + mvwprintw(menuWin, items[i].getPosY(),items[i].getPosX(), items[i].getName().c_str()); + wattroff(menuWin, A_STANDOUT); + } + else + { + mvwprintw(menuWin, items[i].getPosY(),items[i].getPosX(), items[i].getName().c_str()); + } + } + selectedItem = -1; // Deselcting the item } -int Menu::getPosY() +void Menu::selectItem() { - return posY; + +} + +void Menu::findTheLongestItem() +{ + for(i=0; i < itemCount; i++) + { + if(longestItem < items[i].getLength()) + { + longestItem = items[i].getLength(); + } + } } -void Menu::setMenuBarBegX(int x) +void Menu::printMenu() { - this->menuBarBegX = x; + drawBox(); + printItems(); + wrefresh(menuWin); //Refresh so that we can see it +} + +// Handles user input but there shoud be a way to register the selection. +void Menu::handleTrigger(char ch) +{ + for(i=0; i < itemCount; i++) + { + if(ch == items[i].getTrigger()) + { + selectedItem = i; + activeMenu = true; + } + else + { + activeMenu = false; + } + } } -void Menu::setMenuBarBegY(int y) +bool Menu::isActive() { - this->menuBarBegY = y; + return activeMenu; } -void Menu::setMenuBarSizeX(int x) +//Getters +int Menu::getSizeX() { - this->menuBarSizeX = x; + return xSize; } -std::string Menu::getName() + +int Menu::getSizeY() { - return name; + return ySize; } - -int Menu::getNameLenght() + +int Menu::getBegX() { - return name.length(); + return xBeg; } -int Menu::getLongestItemLenght() +int Menu::getBegY() { - return xSize; + return yBeg; } + +int Menu::getLongestItem() +{ + return longestItem; +} + +int Menu::getSelectedItem() +{ + return selectedItem; +} + +int Menu::getOrientation() +{ + return orientation; +} + -char Menu::getTrigger() +//Setters +void Menu::setBegX(int x) +{ + this-> xBeg = x; +} + +void Menu::setBegY(int y) { - return trigger; + this-> yBeg = y; } -int Menu::getBoxBegX() +void Menu::setTopLine(char ch) { - // Vertical Alignment - if(direction) - { - } - else // Horizontal Alignment -> The Deffault - { - return menuBarSizeX+posX+menuBarBegX-1; - } + } -int Menu::getBoxBegY() +void Menu::setBottomLine(char ch) { - // Vertical Alignment - if(direction) - { - } - else // Horizontal Alignment -> The Deffault - { - return posY+menuBarBegY-1; - } + } -void Menu::drawBox() +void Menu::setLeftLine(char ch) { - // Creates a box with caclulated dimention xSize & ySize and user given position - menuItemsWin = newwin(20, 20, getBoxBegY(), getBoxBegX()); - box(menuItemsWin ,0,0); //Here we can define other cahr insterad of 0,0 - mvwprintw(menuItemsWin,1,1,"PosY:%d PosX:%d",getBoxBegY(), getBoxBegX()); - wrefresh(menuItemsWin); //Refresh so that we can see it + } -void Menu::drawItems() +void Menu::setRightLine(char ch) { + +} +char Menu::getKey() +{ + return wgetch(menuWin); } #endif diff --git a/ked/ncurses/training/menuEx/compile_run.sh b/ked/ncurses/training/menuEx/compile_run.sh index e1e0892..7e7b124 100755 --- a/ked/ncurses/training/menuEx/compile_run.sh +++ b/ked/ncurses/training/menuEx/compile_run.sh @@ -2,6 +2,6 @@ rm ./menu -gcc main.c -o menu -lncurses +gcc main.c -o menu -lmenu -lncurses ./menu diff --git a/ked/ncurses/training/menuEx/main.c b/ked/ncurses/training/menuEx/main.c index e06985e..b1d0965 100644 --- a/ked/ncurses/training/menuEx/main.c +++ b/ked/ncurses/training/menuEx/main.c @@ -1,108 +1,100 @@ -#include -#include +#include #include #include -#define WIDTH 30 -#define HEIGHT 10 - -int startx = 0; -int starty = 0; +#include -char *choices[] = { "Choice 1", - "Choice 2", - "Choice 3", - "Choice 4", - "Exit", - }; -int n_choices = sizeof(choices) / sizeof(char *); +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define CTRLD 4 -void print_menu(WINDOW *menu_win, int highlight); -void report_choice(int mouse_x, int mouse_y, int *p_choice); +char *choices[] = { + "Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5", + "Choice 6", "Choice 7", "Choice 8", "Choice 9", "Choice 10", + "Choice 11", "Choice 12", "Choice 13", "Choice 14", "Choice 15", + "Choice 16", "Choice 17", "Choice 18", "Choice 19", "Choice 20", + "Exit", + (char *)NULL, + }; int main() -{ int c, choice = 0; - WINDOW *menu_win; - MEVENT event; +{ ITEM **my_items; + int c; + MENU *my_menu; + WINDOW *my_menu_win; + int n_choices, i; + + /* Initialize curses */ + initscr(); + start_color(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + init_pair(1, COLOR_RED, COLOR_BLACK); + init_pair(2, COLOR_CYAN, COLOR_BLACK); - /* Initialize curses */ - initscr(); - clear(); - noecho(); - cbreak(); //Line buffering disabled. pass on everything + /* Create items */ + n_choices = ARRAY_SIZE(choices); + my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); + for(i = 0; i < n_choices; ++i) + my_items[i] = new_item(choices[i], choices[i]); - /* Try to put the window in the middle of screen */ - startx = (80 - WIDTH) / 2; - starty = (24 - HEIGHT) / 2; - - attron(A_REVERSE); - mvprintw(23, 1, "Click on Exit to quit (Works best in a virtual console)"); - refresh(); - attroff(A_REVERSE); - - /* Print the menu for the first time */ - menu_win = newwin(HEIGHT, WIDTH, starty, startx); - print_menu(menu_win, 1); - /* Get all the mouse events */ - mousemask(ALL_MOUSE_EVENTS, NULL); - - while(1) - { c = wgetch(menu_win); - switch(c) - { case KEY_MOUSE: - if(getmouse(&event) == OK) - { /* When the user clicks left mouse button */ - if(event.bstate & BUTTON1_PRESSED) - { report_choice(event.x + 1, event.y + 1, &choice); - if(choice == -1) //Exit chosen - goto end; - mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]); - refresh(); - } - } - print_menu(menu_win, choice); - break; - } - } -end: - endwin(); - return 0; -} + /* Crate menu */ + my_menu = new_menu((ITEM **)my_items); + /* Set menu option not to show the description */ + menu_opts_off(my_menu, O_SHOWDESC); -void print_menu(WINDOW *menu_win, int highlight) -{ - int x, y, i; + /* Create the window to be associated with the menu */ + my_menu_win = newwin(10, 70, 4, 4); + keypad(my_menu_win, TRUE); + + /* Set main window and sub window */ + set_menu_win(my_menu, my_menu_win); + set_menu_sub(my_menu, derwin(my_menu_win, 6, 68, 3, 1)); + set_menu_format(my_menu, 5, 3); + set_menu_mark(my_menu, " * "); - x = 2; - y = 2; - box(menu_win, 0, 0); - for(i = 0; i < n_choices; ++i) - { if(highlight == i + 1) - { wattron(menu_win, A_REVERSE); - mvwprintw(menu_win, y, x, "%s", choices[i]); - wattroff(menu_win, A_REVERSE); - } - else - mvwprintw(menu_win, y, x, "%s", choices[i]); - ++y; - } - wrefresh(menu_win); -} + /* Print a border around the main window and print a title */ + box(my_menu_win, 0, 0); + + attron(COLOR_PAIR(2)); + mvprintw(LINES - 3, 0, "Use PageUp and PageDown to scroll"); + mvprintw(LINES - 2, 0, "Use Arrow Keys to navigate (F1 to Exit)"); + attroff(COLOR_PAIR(2)); + refresh(); -/* Report the choice according to mouse position */ -void report_choice(int mouse_x, int mouse_y, int *p_choice) -{ int i,j, choice; + /* Post the menu */ + post_menu(my_menu); + wrefresh(my_menu_win); + + while((c = wgetch(my_menu_win)) != KEY_F(1)) + { switch(c) + { case KEY_DOWN: + menu_driver(my_menu, REQ_DOWN_ITEM); + break; + case KEY_UP: + menu_driver(my_menu, REQ_UP_ITEM); + break; + case KEY_LEFT: + menu_driver(my_menu, REQ_LEFT_ITEM); + break; + case KEY_RIGHT: + menu_driver(my_menu, REQ_RIGHT_ITEM); + break; + case KEY_NPAGE: + menu_driver(my_menu, REQ_SCR_DPAGE); + break; + case KEY_PPAGE: + menu_driver(my_menu, REQ_SCR_UPAGE); + break; + } + wrefresh(my_menu_win); + } - i = startx + 2; - j = starty + 3; - - for(choice = 0; choice < n_choices; ++choice) - if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice])) - { if(choice == n_choices - 1) - *p_choice = -1; - else - *p_choice = choice + 1; - break; - } + /* Unpost and free all the memory taken up */ + unpost_menu(my_menu); + free_menu(my_menu); + for(i = 0; i < n_choices; ++i) + free_item(my_items[i]); + endwin(); } diff --git a/ked/ncurses/training/menuEx/menu b/ked/ncurses/training/menuEx/menu index 05b4741f07ec03d7e8ea26a8fb424abd96e270a3..dbce112ce9f4978d8a936b6097c61fc0341983f9 100755 GIT binary patch literal 18664 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 Cfxkci literal 17864 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