From 7e8b9b557033b178857e9bc81742348f4ecdfc78 Mon Sep 17 00:00:00 2001 From: kpl Date: Tue, 30 Oct 2012 12:42:21 -0700 Subject: [PATCH] Initial commit --- Makefile.am | 9 +- configure.ac | 4 +- src/cmflapp/Fl_Splitter.cpp | 265 +++++++++++++++++ src/cmflapp/Fl_Splitter.h | 74 +++++ src/cmflapp/app.cpp | 555 ++++++++++++++++++++++++++++++++++++ src/cmflapp/app.h | 119 ++++++++ src/cmflapp/appErr.h | 24 ++ src/cmflapp/main.cpp | 110 +++++++ src/proj/main.c | 39 --- 9 files changed, 1154 insertions(+), 45 deletions(-) create mode 100644 src/cmflapp/Fl_Splitter.cpp create mode 100644 src/cmflapp/Fl_Splitter.h create mode 100644 src/cmflapp/app.cpp create mode 100644 src/cmflapp/app.h create mode 100644 src/cmflapp/appErr.h create mode 100644 src/cmflapp/main.cpp delete mode 100644 src/proj/main.c diff --git a/Makefile.am b/Makefile.am index 1d11e5b..aa2df76 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,7 +15,7 @@ AM_CPPFLAGS = -D _GNU_SOURCE -I.. -I$(srcdir)/src/libcm -I$(srcdir)/src/libcm/ AM_CFLAGS = -Wno-multichar AM_CXXFLAGS = AM_LDFLAGS = -MYLIBS = -lpthread -lfftw3f -lfftw3 +MYLIBS = -lpthread -lfftw3f -lfftw3 -lfltk CMLIBS = src/libcm/libcm.la # autoconfig manual recommends storing direct referenes to non-3rd party libraries rather than using -L and -l @@ -56,6 +56,7 @@ include_HEADERS += $(cmHDR) lib_LTLIBRARIES += src/libcm/libcm.la -src_proj_proj_SOURCES = src/proj/main.c -src_proj_proj_LDADD = $(CMLIBS) $(MYLIBS) -bin_PROGRAMS += src/proj/proj +src_cmflapp_cmflapp_SOURCES = src/cmflapp/main.cpp src/cmflapp/app.h src/cmflapp/app.cpp +src_cmflapp_cmflapp_SOURCES += src/cmflapp/Fl_Splitter.h src/cmflapp/Fl_Splitter.cpp +src_cmflapp_cmflapp_LDADD = $(CMLIBS) $(MYLIBS) +bin_PROGRAMS += src/cmflapp/cmflapp diff --git a/configure.ac b/configure.ac index 88c6d3f..e941704 100644 --- a/configure.ac +++ b/configure.ac @@ -3,10 +3,10 @@ # this configure.ac or any of the Makefile.am files. # -AC_INIT([proj],[1.0],[proj@larke.org]) +AC_INIT([cmflapp],[1.0],[cmflapp@larke.org]) AC_CONFIG_AUX_DIR([build-aux]) # put aux files in build-aux AM_INIT_AUTOMAKE([1.9 -Wall foreign subdir-objects]) # subdir-objects needed for non-recursive make -AC_CONFIG_SRCDIR([src/proj/main.c]) +AC_CONFIG_SRCDIR([src/cmflapp/main.cpp]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/src/cmflapp/Fl_Splitter.cpp b/src/cmflapp/Fl_Splitter.cpp new file mode 100644 index 0000000..b85ca05 --- /dev/null +++ b/src/cmflapp/Fl_Splitter.cpp @@ -0,0 +1,265 @@ +// Code based on: http://www.mail-archive.com/fltk@easysw.com/msg04573.html +// Lucas Sanner/Ian MacArthur + +#include "Fl_Splitter.h" + +void Fl_HSplitter::draw() +{ + Fl_Group::draw(); + fl_color(FL_BLACK); + fl_line_style( FL_DOT, 1); + + if(bSplit && Fl::event_button1() != 0) + { + fl_push_clip(x(), y(), w(), h()); + + yPos = Fl::event_y(); + + if(Fl::event_y() > h() - (border * 2)) + yPos = h() - (border * 2); + + if(Fl::event_y() < y() + (border * 2)) + yPos = y() + (border * 2); + + fl_line( x() + border, yPos - 1, x() + w() - (border*2), yPos - 1 ); + fl_line( x() + border, yPos , x() + w() - (border*2), yPos ); + fl_line( x() + border, yPos + 1, x() + w() - (border*2), yPos + 1 ); + + /* + fl_line( + x() + border, + yPos, + x() + w() - (border*2), + yPos ); + + fl_line( + x() + border, + yPos + 1, + x() + w() - (border*2), + yPos + 1 ); + */ + + fl_pop_clip(); + } + else + { + fl_push_clip(x(), y(), w(), h()); + fl_pop_clip(); + } +} + +int Fl_HSplitter::handle(int e) +{ + int ret = Fl_Group::handle(e); + + switch(e) + { + case FL_MOVE: + + if(Fl::event_y() > hCtnl - border && Fl::event_y() < hCtnl + border) + { + fl_cursor(FL_CURSOR_NS); + bSplit = true; + } + else + { + fl_cursor(FL_CURSOR_DEFAULT); + bSplit = false; + } + + return 1; + + case FL_PUSH: + + if(Fl::event_button() == FL_LEFT_MOUSE && bSplit) + { + redraw(); + } + + return 1; + + case FL_RELEASE: + + if(Fl::event_button() == FL_LEFT_MOUSE && bSplit) + { + container1->resize(x(), y(), w(), yPos - y()); + hCtnl = yPos; + container2->resize(x(),hCtnl, w(), h() - (yPos - y())); + + if( stretchTopFl ) + init_sizes(); + + bSplit = false; + redraw(); + } + + return 1; + + case FL_DRAG: + + if(bSplit && Fl::event_state(FL_BUTTON1) != 0) + redraw(); + + return 1; + } + + return(ret); +} + + +Fl_HSplitter::Fl_HSplitter(int x, int y, int w, int h, int h1, const char *l, bool stretchBotFl) + : Fl_Group(x, y, w, h, l) +{ + int h2 = h - h1; + + begin(); + container1 = new Splitter_Container(x, y, w, h1); + //container1->color((Fl_Color) FL_RED); + end(); + + begin(); + container2 = new Splitter_Container(x, y + h1, w, h2); + //container2->color((Fl_Color) FL_BLUE); + end(); + + hCtnl = y + h1; + bSplit = false; + stretchTopFl = !stretchBotFl; + + border = Fl::box_dy(FL_DOWN_BOX); + + if( stretchTopFl ) + resizable(container1); +} + +void Fl_HSplitter::resize_splitter(int x, int y, int w, int h) +{ + resize(x, y, w, h); + + if( stretchTopFl ) + { + hCtnl = container1->h() + y; + yPos = hCtnl; + } + else + { + container1->resize(x, y, w, hCtnl - y); + container2->resize(x, hCtnl, w, h - (hCtnl - y)); + } + +} + + + +void Fl_VSplitter::draw() +{ + Fl_Group::draw(); + fl_color(FL_BLACK); + fl_line_style( FL_DOT, 1); + + if(bSplit && Fl::event_button1() != 0) + { + fl_push_clip(x(), y(), w(), h()); + + xPos = Fl::event_x(); + + if(Fl::event_x() > w() - (border * 2)) + xPos = w() - (border * 2); + + if(Fl::event_x() < x() + (border * 2)) + xPos = x() + (border * 2); + + fl_line(xPos - 1, y() + border, xPos - 1, y() + h() - (border * 2)); + fl_line(xPos, y() + border, xPos, y() + h() - (border * 2)); + fl_line(xPos + 1, y() + border, xPos + 1, y() + h() - (border * 2)); + + fl_pop_clip(); + } + else + { + fl_push_clip(x(), y(), w(), h()); + fl_pop_clip(); + } +} + +int Fl_VSplitter::handle(int e) +{ + int ret = Fl_Group::handle(e); + + switch(e) + { + case FL_MOVE: + + if(Fl::event_x() > wCtn1 - border && Fl::event_x() < wCtn1 + border) + { + fl_cursor(FL_CURSOR_WE); + bSplit = true; + } + else + { + fl_cursor(FL_CURSOR_DEFAULT); + bSplit = false; + } + + return 1; + + case FL_PUSH: + + if(Fl::event_button() == FL_LEFT_MOUSE && bSplit) + { + redraw(); + } + + return 1; + + case FL_RELEASE: + + if(Fl::event_button() == FL_LEFT_MOUSE && bSplit) + { + container1->resize(x(), y(), xPos, h()); + wCtn1 = xPos; + container2->resize(wCtn1, y(), w() - wCtn1, h()); + bSplit = false; + redraw(); + } + + return 1; + + case FL_DRAG: + + if(bSplit && Fl::event_state(FL_BUTTON1) != 0) + redraw(); + + return 1; + } + + return(ret); +} + +Fl_VSplitter::Fl_VSplitter(int x, int y, int w, int h, const char *l) + : Fl_Group(x, y, w, h, l) +{ + begin(); + container1 = new Splitter_Container(x, y, w / 2, h); + //container1->color((Fl_Color) FL_RED); + end(); + + begin(); + container2 = new Splitter_Container(x + (w / 2), y, w / 2, h); + //container2->color((Fl_Color) FL_BLUE); + end(); + + wCtn1 = w / 2; + bSplit = false; + + border = Fl::box_dx(FL_DOWN_BOX); + +} + +void Fl_VSplitter::resize_splitter(int x, int y, int w, int h) +{ + resize(x, y, w, h); + container1->resize(x, y, wCtn1, h); + container2->resize(wCtn1, y, w - wCtn1, h); +} + diff --git a/src/cmflapp/Fl_Splitter.h b/src/cmflapp/Fl_Splitter.h new file mode 100644 index 0000000..3fef293 --- /dev/null +++ b/src/cmflapp/Fl_Splitter.h @@ -0,0 +1,74 @@ +// Code based on: http://www.mail-archive.com/fltk@easysw.com/msg04573.html +// Lucas Sanner/Ian MacArthur + +#ifndef Fl_Splitter_h +#define Fl_Splitter_h + +#include +#include +#include +#include + + +class Splitter_Container : public Fl_Group +{ + void draw() + { + fl_push_clip(x(), y(), w(), h()); + Fl_Group::draw(); + fl_pop_clip(); + } + + public: + + Splitter_Container(int x, int y, int w, int h, char *l = NULL) + : Fl_Group(x, y, w, h, l) + { + //box(FL_DOWN_BOX); + } +}; + +class Fl_HSplitter : public Fl_Group +{ + int hCtnl, border, yPos; + bool bSplit; + bool stretchTopFl; // set to make top container stretch when splitter is resized + int hh2; + + void draw(); + + int handle(int e); + + public: + + Splitter_Container *container1, *container2; + + Fl_HSplitter(int x, int y, int w, int h, int h1, const char *l=NULL, bool stretchBotFl=false); + + void resize_splitter(int x, int y, int w, int h); + +}; + + +class Fl_VSplitter : public Fl_Group +{ + + int wCtn1, border, xPos; + bool bSplit; + + void draw(); + int handle(int e); + + public: + + Splitter_Container *container1, *container2; + + Fl_VSplitter(int x, int y, int w, int h, const char *l = NULL); + + void resize_splitter(int x, int y, int w, int h); + + + +}; + +#endif diff --git a/src/cmflapp/app.cpp b/src/cmflapp/app.cpp new file mode 100644 index 0000000..29dd6a6 --- /dev/null +++ b/src/cmflapp/app.cpp @@ -0,0 +1,555 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Fl_Splitter.h" + +#include "cmPrefix.h" +#include "cmGlobal.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmFileSys.h" +#include "cmThread.h" + +#include "appErr.h" +#include "app.h" + +class drawWnd : public Fl_Widget +{ +public: + drawWnd(app* ap, int x, int y, int w, int h, const char* label = NULL ); + virtual ~drawWnd(); + + virtual int handle(int event); + virtual void resize(int x, int y, int w, int h ); + +protected: + virtual void draw(); + +private: + app* _app; +}; + +drawWnd::drawWnd(app* ap, int x, int y, int w, int h, const char* label ) + : Fl_Widget(x,y,w,h,label), + _app(ap) +{} + +drawWnd::~drawWnd() +{} + +int drawWnd::handle(int event) +{ + switch(event) + { + case FL_PUSH: + { + const char* label; + switch( Fl::event_button() ) + { + case FL_LEFT_MOUSE: label = "left"; break; + case FL_RIGHT_MOUSE: label = "right"; break; + case FL_MIDDLE_MOUSE: label = "middle"; break; + default: + label = "none"; + } + + _app->print("%i %i %s\n",Fl::event_x(),Fl::event_y(),label); + } + break; + + default: + return Fl_Widget::handle(event); + } + + return 1; +} + +void drawWnd::resize(int x, int y, int w, int h ) +{ + // must call base to make size change + Fl_Widget::resize(x,y,w,h); +} + +void drawWnd::draw() +{ + int offs = 10; + //fl_draw_box(FL_DOWN_BOX,x()+10,y()+10,w()-20,h()-20,FL_RED); + //fl_frame("XXXX",x()+offs,y()+offs,w()-2*offs,h()-2*offs); + fl_line_style(FL_SOLID,1,NULL); + fl_color(FL_RED); + fl_line(x()+offs,y()+offs,x()+w()-offs,y()+h()-offs); + + + int x = w()/2; + int y = h()/2; + + fl_color(FL_BLACK); + fl_draw(90,"string",x,y); + + // XXXXXXX + // X00000X + // X00000X + // X00X00X + // X00000X + // X00000X + // XXXXXXX + + fl_rect(x-3,y-3,7,7); + fl_point(x,y); +} + +//---------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------- + +app::app(cmCtx_t* ctx, cmTsMp1cH_t printqH, int w, int h, const char* label, int argc, char* argv[]) + : Fl_Double_Window(w,h,label), _timerPeriodSecs(0), + _ctx(ctx), _splt(NULL),_buf(NULL),_con(NULL),_tabs(NULL),_menu(NULL), + _printqH(printqH),_printFl(0) +{ + + // install a callback to cleanup when the app window closes + // (the btn and menu callbacks also rely on a pointer to 'this' being found in app.user_data() + // see _getApp()) + callback(_s_callback,this); + + // the main window is divided between the menu bar on top + // and a horizontal splitter on the bottom + begin(); + createMenu(w,kMenuH); + _splt = new Fl_HSplitter(0, kMenuH, w, h-kStatusH, h-kStatusH-100); + end(); + + // Create a text display object for console output and + // add it to the lower splitter area + _buf = new Fl_Text_Buffer(); + _con = new Fl_Text_Display(_splt->container2->x(),_splt->container2->y(),_splt->container2->w(),_splt->container2->h()); + _con->buffer(_buf); + _splt->container2->add(_con); + + // create tabbed windows + const char* titles[] = { "Title 1", "Title 2" }; + _create_tabs(2,titles); + + // make the splitter the resizable group element (thereby making the menu non-resizable). + // see:http://fltk.org/articles.php?L415+I0+T+M1000+P1 + resizable(_splt); + show(argc, argv); + + // The application is now visible. + // direct all output to the console window + cmRptSetup(&_ctx->rpt,_s_print,_s_print, this); + cmTsMp1cSetCbFunc(_printqH, _s_print_queue_cb, this ); + + + // install a timer + _timerPeriodSecs = 0.1; + Fl::add_timeout(_timerPeriodSecs,app::_s_timer_cb,this); + + // install an idle callback + //Fl::add_idle(_s_idle_cb,this); + + print("Started!\n"); +} + +app::~app() +{} + +void app::createControls(Fl_Group* grp) +{ + const int vBord = 3; + const int ctl_width = 100; + int xx = grp->x() + 5; + int yy = grp->y() + vBord; + + Fl_Check_Button* cbt = new Fl_Check_Button(xx,yy,ctl_width,kCtlH,"Check"); + cbt->callback( _s_btn_cb, kBtn1MId ); + yy += cbt->h() + vBord; + + Fl_Button* btn = new Fl_Button(xx,yy,ctl_width,kCtlH,"Button"); + btn->callback( _s_btn_cb, kBtn2MId ); + yy += btn->h() + vBord; + + // menu buttons callback through menuCallback() not btnCallback() + Fl_Menu_Button* mbt = new Fl_Menu_Button(xx,yy,ctl_width,kCtlH,"Menu"); + + mbt->add("Item 1",0,_s_menu_btn_cb, (void*)kMenuBtn1MId, 0); + mbt->add("Item 2",0,_s_menu_btn_cb, (void*)kMenuBtn2MId, 0); + yy += mbt->h() + vBord; + + Fl_Value_Input* vip = new Fl_Value_Input(xx,yy,ctl_width/2,kCtlH,"Value In"); + vip->callback(_s_value_cb, kValue1MId ); + vip->align(FL_ALIGN_RIGHT); // place label to the right of the input ctl + // Only make the callback when the enter key is struck or the ctl loses focus and the value has changed. + // Removing this line causes the callback whenever the input changes. + vip->when(FL_WHEN_ENTER_KEY | FL_WHEN_RELEASE ); + vip->bounds(-10.0,10.0); + vip->step(0.1); + yy += vip->h() + vBord; + + Fl_Value_Slider* sdp = new Fl_Value_Slider(xx,yy,ctl_width,kCtlH,"Slider"); + sdp->callback(_s_value_cb, kValue2MId ); + sdp->align(FL_ALIGN_RIGHT); + sdp->type(FL_HOR_NICE_SLIDER); + sdp->bounds(-10.0,10.0); + sdp->step(0.1); + yy += sdp->h() + vBord; + + Fl_Input* inp = new Fl_Input(xx,yy,ctl_width/2,kCtlH,"Text"); + inp->callback(_s_input_cb,kInput1MId); + inp->align(FL_ALIGN_RIGHT); + yy += inp->h() + vBord; + + +} + + +void app::initializeTab(int tabIndex, int x, int y, int w, int h, const char* label) +{ + switch(tabIndex) + { + case 0: + { + Fl_Group* grp = new Fl_Group(x,y,w,h,label); + grp->begin(); + createControls(grp); + grp->end(); + } + break; + + case 1: + { + Fl_Group* grp = new Fl_Group(x,y,w,h,label); + grp->begin(); + _draw = new drawWnd(this,x,y,w,h,NULL); + grp->end(); + } + break; + } + +} + +void app::createMenu(int w, int h) +{ + Fl_Menu_Item items[] = + { + { "&File", 0, 0, 0, FL_SUBMENU }, + { "&New File", FL_COMMAND + 'n', (Fl_Callback*)_s_menu_cb, (void*)kFileNewMId }, + { "&Open File", FL_COMMAND + 'o', (Fl_Callback*)_s_menu_cb, (void*)kFileOpenMId }, + { 0 }, + { "&Edit", 0, 0, 0, FL_SUBMENU }, + { "&Copy", FL_COMMAND + 'c', (Fl_Callback*)_s_menu_cb, (void*)kEditCopyMId }, + { "&Paste", FL_COMMAND + 'v', (Fl_Callback*)_s_menu_cb, (void*)kEditPasteMId }, + { 0 }, + { 0 } + }; + + _menu = new Fl_Menu_Bar(0,0,w,h); + _menu->copy(items); +} + +void app::menuCallback(const Fl_Menu_Item* mip, long int id) +{ + switch(id) + { + case kFileOpenMId: + { + // file chooser demo + + const char* pathStr = cmFsUserDir(); // make the default dir the user's home dir + const char patStr[] = "Text Files (*.txt)\tAudio Files (*.{wav,aif,aiff})"; // All Files (*.*) is append to this automatically + const char titleStr[] = "Select files"; + + Fl_File_Chooser fc = Fl_File_Chooser(pathStr,patStr,Fl_File_Chooser::MULTI,titleStr); + + fc.preview(0); // default the previous option to 'off'. + fc.show(); // show the chooser + + // make the chooser modal + while( fc.shown() ) + Fl::wait(); + + // print the selected files + int i; + for(i=1; i<=fc.count(); ++i) + print("%i %s\n",i,cmStringNullGuard(fc.value(i))); + + } + break; + } + + print("%i %s\n",id,mip->label()); +} + +void app::btnCallback(const Fl_Button* bp, int id ) +{ + print("%i %s %i\n",id,bp->label(),bp->value()); +} + +void app::valueCallback(const Fl_Valuator* vp, int id ) +{ + print("%i %s %f\n",id,vp->label(),vp->value()); +} + +void app::inputCallback(const Fl_Input* ip, int id ) +{ + print("%i %s %s\n",id,ip->label(),ip->value()); +} + +void app::appCallback(Fl_Widget* wp) +{ + switch( Fl::event() ) + { + case FL_CLOSE: + { + + /* + int cc = _closeCnt; + + // attempt to shut down the pgm + _closeCnt += finalizePgm() == false; + + // the first time the pgm fails to shut down + // do not allow the pgm to close the main window + // this will give a chance for the error messages + // to be diplayed in the console - all successive + // times the return value from finalizePgm() is + + // ignored and the program is terminated. + if( _closeCnt == 1 && cc==0) + { + // send a strong hint that a problem occurred on shutdown + // and prevent the app from receiving events that might cause it to + // crash + deactivate(); + return; + } + */ + + // When all windows are windows are closed then the app. + // will close - so hiding the application window + // causes the program to close. + // + // Note that simply returning from this callback will + // prevent the application from closing. Because the existence + // of the callback alone is enough to disable default + // event handling. + hide(); + } + break; + } + +} +bool app::idleCallback() +{ return true; } + + +bool app::timerCallback() +{ + _checkPrintQueue(); + return true; +} + +void app::error( const char* fmt, ... ) +{ + va_list vl; + va_start(vl,fmt); + + int bufCharCnt = 511; + char buf[bufCharCnt+1]; + + int n = vsnprintf(buf,bufCharCnt,fmt,vl); + + if( n > 0 ) + print("%s Error: %s\n",label(),buf); + + va_end(vl); +} + +void app::vprint(const char* fmt, va_list vl ) +{ + int bufCharCnt = 511; + char buf[bufCharCnt+1]; + + int n = vsnprintf(buf,bufCharCnt,fmt,vl); + + if( n > 0 ) + { + // if the print queue exists (it might not during startup or shutdown) ... + if( cmTsMp1cIsValid(_printqH) ) + { + cmThRC_t thRC; + + // ... enqueue the text to print + if((thRC = cmTsMp1cEnqueueMsg(_printqH,buf,n+1)) != kOkThRC && _printFl==0 ) + { + // Print queue error msg's directly to stdout rather than throught + // the error mechanism - this prevents generating a stack overflow + // when the print queue runs out of space and attempts to print a + // 'queue out of memory' error msg. + // The queue itself does will not print errors because it does + // not have a valid cmRpt_t pointer. See cmMp1cCreate() above. + ++_printFl; + cmErrMsg(&_ctx->err,kQueueFailAppRC,"Print enqueue failed."); + --_printFl; + } + } + else + _print(buf); // ... otherwise just send the text directly to the output console + } + +} + +void app::print( const char* fmt, ... ) +{ + va_list vl; + va_start(vl,fmt); + vprint(fmt,vl); + va_end(vl); +} + +void app::_s_menu_cb(Fl_Widget* wp, void* data) +{ + const Fl_Menu_Item* mip; + app* ap; + + if((ap=_getApp(wp)) != NULL ) + if((mip = ap->_menu->mvalue()) != NULL ) + ap->menuCallback(mip,(long int)mip->user_data()); +} + +void app::_s_menu_btn_cb(Fl_Widget* wp, void* data) +{ + const Fl_Menu_Button* bp; + const Fl_Menu_Item* mip; + app* ap; + + if((ap=_getApp(wp)) != NULL ) + if((bp = static_cast(wp)) != NULL ) + if((mip = bp->mvalue()) != NULL) + ap->menuCallback(mip,(long int)data); +} + +void app::_s_btn_cb(Fl_Widget* wp, long data ) +{ + app* ap; + + if((ap = _getApp(wp)) != NULL ) + if( wp != NULL ) + ap->btnCallback( static_cast(wp),data); +} + +void app::_s_value_cb( Fl_Widget* wp, long data) +{ + app* ap; + Fl_Valuator* vp; + + if((ap = _getApp(wp)) != NULL ) + if((vp = static_cast(wp)) != NULL) + ap->valueCallback(vp,data); +} + +void app::_s_input_cb( Fl_Widget* wp, long data) +{ + app* ap; + Fl_Input* inp; + + if((ap = _getApp(wp)) != NULL) + if((inp = static_cast(wp)) != NULL ) + ap->inputCallback(inp,data); +} + +void app::_s_callback(Fl_Widget* wp, void* data) +{ ((app*)data)->appCallback(wp); } + +void app::_s_idle_cb(void *data) +{ + app* thisPtr = (app*)data; + + if( thisPtr->idleCallback() == false ) + Fl::remove_idle(_s_idle_cb,data); +} + +void app::_s_timer_cb(void* userPtr) +{ + app* thisPtr = (app*)userPtr; + + if( thisPtr->timerCallback() ) + Fl::repeat_timeout(thisPtr->_timerPeriodSecs,_s_timer_cb,userPtr); +} + +void app::_s_print( void* userPtr, const char* text ) +{ ((app*)userPtr)->print(text); } + + +void app::_create_tabs(int titleCnt, const char* titleArray[]) +{ + // Create a tab view and added it to the upper splitter area + _tabs = new Fl_Tabs(_splt->container1->x(),_splt->container1->y(),_splt->container1->w(),_splt->container1->h()); + _splt->container1->add(_tabs); + + int tx,ty,th,tw,i; + _tabs->client_area(tx,ty,tw,th); + + for(i=0; ibegin(); + initializeTab(i,tx,ty,tw,th,titleArray[i]); + _tabs->end(); + } + + // Create an empty tab group and make it resizable + // to prevent the other tab groups from being resizable. + _tabs->begin(); + _tabs->resizable(new Fl_Group(tx,ty+30,1,1)); + _tabs->end(); +} + +app* app::_getApp( Fl_Widget* w ) +{ + // walk up the widget tree until the top widget is found + Fl_Group* gp = w->parent(); + while( gp->parent() != NULL ) + gp=gp->parent(); + + // the user data for the top widget is a pointer app - as set in: callback(_s_callback,this); + return (app*)gp->user_data(); +} + +cmRC_t app::_s_print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ) +{ + app* ap = (app*)userCbPtr; + ap->_print((const char*)msgDataPtr); + return cmOkRC; +} + +void app::_checkPrintQueue() +{ + while( cmTsMp1cMsgWaiting(_printqH) ) + if( cmTsMp1cDequeueMsg(_printqH, NULL, 0) != kOkThRC ) + error("Print dequeue failed."); +} + +void app::_print( const char* text ) +{ + if( _con != NULL ) + _con->insert(text); +} diff --git a/src/cmflapp/app.h b/src/cmflapp/app.h new file mode 100644 index 0000000..e887159 --- /dev/null +++ b/src/cmflapp/app.h @@ -0,0 +1,119 @@ +#ifndef app_h +#define app_h + +class Fl_HSplitter; +class Fl_Text_Buffer; +class Fl_Text_Display; +class Fl_Box; +class Fl_Tabs; +struct Fl_Menu_Item; +class Fl_Menu_Bar; +class Fl_Group; +class Fl_Button; +class Fl_Valuator; +class Fl_Input; +class drawWnd; + +class app : public Fl_Double_Window +{ + +public: + + // widget id's for widgets created by createControls() + enum + { + kFileOpenMId, + kFileNewMId, + kEditCopyMId, + kEditPasteMId, + + kBtn1MId, + kBtn2MId, + kBtn3MId, + + kMenuBtn1MId, + kMenuBtn2MId, + + kValue1MId, + kValue2MId, + + kInput1MId + }; + + app(cmCtx_t* ctx, cmTsMp1cH_t printqH, int w, int h, const char *l, int argc, char *argv[]); + virtual ~app(); + + // Example tabbed window initialization example function + // which also demonstrates some widgets + void createControls(Fl_Group* grp); + + // called once from app() to initialize each tabbed window + virtual void initializeTab(int tabIndex, int x, int y, int w, int h, const char* label); + + // Create the application menu + virtual void createMenu( int menuWidth, int menuHeight ); + + // Widget type specific callbacks + virtual void menuCallback( const Fl_Menu_Item* ip, long int id ); // menu and menu btn callback + virtual void btnCallback( const Fl_Button* bp, int id ); // btn callback + virtual void valueCallback(const Fl_Valuator* vp, int id ); // valuator callback + virtual void inputCallback(const Fl_Input* ip, int id ); // text input callback + + virtual void appCallback(Fl_Widget* wp); // app event callback + virtual bool idleCallback(); // return false to stop callback + virtual bool timerCallback(); // return false to stop callback + + void error( const char* fmt, ... ); // report on error + + // print to the console + virtual void vprint( const char* fmt, va_list vl ); + virtual void print( const char* fmt, ... ); + +protected: + enum + { + kStatusH = 30, + kMenuH = 30, + kCtlH = 30 + }; + + // static callbacks + static void _s_menu_cb( Fl_Widget* wp, void* data); // menu item callback + static void _s_menu_btn_cb(Fl_Widget* wp, void* data); // menu btn callback + static void _s_btn_cb( Fl_Widget* wp, long data); // button callback + static void _s_value_cb( Fl_Widget* wp, long data); // value input or slider callback + static void _s_input_cb( Fl_Widget* wp, long data); // text input callback + static void _s_callback( Fl_Widget* wp, void* data); // main app callback + + static void _s_idle_cb( void* data); // idle callback + static void _s_timer_cb( void* userPtr); // timer callback + static void _s_print( void* userPtr, const char* text ); // print text to the console + + void _create_tabs(int titleCnt, const char* titleArray[]); + + // walk down the widget tree until the app (root) widget is located + static app* _getApp( Fl_Widget* w ); + + // called internally by cmTsQueueDequeue() to send text to _print() + static cmRC_t _s_print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ); + + // called periodically to check the print queue for waiting text. + void _checkPrintQueue(); + + // send a string of text directly to the output console window + void _print( const char* text ); + + double _timerPeriodSecs; // repeat period for the timer callback + + cmCtx_t* _ctx; // + Fl_HSplitter* _splt; // main splitter window + Fl_Text_Buffer* _buf; // text buffer used by _con + Fl_Text_Display* _con; // text console output window + Fl_Tabs* _tabs; // tabs window + Fl_Menu_Bar* _menu; // app. menu + drawWnd* _draw; // custom widget + cmTsMp1cH_t _printqH; // thread-safe queue for controlling access to the output console from multiple threads + int _printFl; // used to prevent recursion due to throwing printing error +}; + +#endif diff --git a/src/cmflapp/appErr.h b/src/cmflapp/appErr.h new file mode 100644 index 0000000..99db748 --- /dev/null +++ b/src/cmflapp/appErr.h @@ -0,0 +1,24 @@ +#ifndef appErr_h +#define appErr_h + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + kOkAppRC, + kQueueFailAppRC, + kFileSysFailAppRC, + kMemFailAppRC, + kTextSysFailAppRC, + kPrefsFailAppRC +}; + + typedef unsigned cmAppRC_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cmflapp/main.cpp b/src/cmflapp/main.cpp new file mode 100644 index 0000000..34bb1d9 --- /dev/null +++ b/src/cmflapp/main.cpp @@ -0,0 +1,110 @@ +#include +#include +#include + +#include "cmPrefix.h" +#include "cmGlobal.h" +#include "cmFloatTypes.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmLinkedHeap.h" +#include "cmThread.h" +#include "cmFileSys.h" +#include "cmText.h" + +#include "appErr.h" +#include "app.h" + + + + +void print( void*, const cmChar_t* text) +{ puts(text); } + +cmRC_t print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ) +{ + print(NULL,(const char*)msgDataPtr); + return cmOkRC; +} + + +int main( int argc, char* argv[] ) +{ + cmCtx_t ctx; + cmTsMp1cH_t printqH = cmTsMp1cNullHandle; + int appWndW = 1000; + int appWndH = 750; + const char* appPrefDir = "gv"; + const char* appTitle = "GV Console"; + bool memDebugFl = cmDEBUG_FL; + unsigned memPadByteCnt = memDebugFl ? 8 : 0; + unsigned memAlignByteCnt = 16; + unsigned memFlags = memDebugFl ? (kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl) : 0; + + cmCtxSetup(&ctx,appTitle,print,print,NULL,memPadByteCnt,memAlignByteCnt,memFlags); + + // initialize the memory mgr + if(cmMdInitialize( memPadByteCnt, memAlignByteCnt, memFlags, &ctx.rpt ) != kOkMmRC ) + { + cmErrMsg(&ctx.err,kMemFailAppRC,"Heap initialization failed."); + goto errLabel; + } + + // initialize the file system + if( cmFsInitialize( &ctx, appPrefDir ) != kOkFsRC ) + cmErrMsg(&ctx.err,kFileSysFailAppRC,"File system initialization failed."); + + // initialize the text system + if( cmTsInitialize(&ctx) != kOkTxRC ) + cmErrMsg(&ctx.err,kTextSysFailAppRC,"Text system initialization failed."); + + // create the print queue + if( cmTsMp1cCreate( &printqH, 8192, print_queue_cb, NULL, NULL ) != kOkThRC ) + cmErrMsg(&ctx.err,kQueueFailAppRC,"Print queue creation failed."); + + + if( cmErrLastRC(&ctx.err) == kOkAppRC ) + { + app proj(&ctx, printqH, appWndW, appWndH, appTitle, argc, argv); + Fl::run(); + + // reset the pgm context and print queue console output to stdout + cmRptSetup(&ctx.rpt,print,print,NULL); + cmTsMp1cSetCbFunc(printqH, print_queue_cb, NULL ); + } + + // empty any pending text to stdout + while( cmTsMp1cMsgWaiting(printqH) ) + if( cmTsMp1cDequeueMsg(printqH, NULL, 0) != kOkThRC ) + cmErrMsg(&ctx.err,kQueueFailAppRC,"Print dequeue failed."); + + // destroy the print queue + if( cmTsMp1cDestroy(&printqH) != kOkThRC ) + cmErrMsg(&ctx.err,kQueueFailAppRC,"Print queue destroy failed."); + + // finalize the text system + if( cmTsFinalize() != kOkTxRC ) + cmErrMsg(&ctx.err,kTextSysFailAppRC,"Text system finalization failed."); + + // finalize the file system + if( cmFsFinalize() != kOkFsRC ) + cmErrMsg(&ctx.err,kFileSysFailAppRC,"File system finalize failed."); + + // report memory mgr errors + if( cmMdIsValid() ) + cmMdReport( kIgnoreNormalMmFl ); + + // finalize the memory manager + if( cmMdFinalize() != kOkMmRC ) + cmErrMsg(&ctx.err,kMemFailAppRC,"Heap finalization failed."); + + errLabel: + + + + + return 0; +} diff --git a/src/proj/main.c b/src/proj/main.c deleted file mode 100644 index 19ff308..0000000 --- a/src/proj/main.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "cmPrefix.h" -#include "cmGlobal.h" -#include "cmRpt.h" -#include "cmErr.h" -#include "cmCtx.h" -#include "cmMem.h" -#include "cmMallocDebug.h" -#include "cmLinkedHeap.h" -#include "cmFileSys.h" -#include "cmText.h" - -void print( void* arg, const char* text ) -{ - printf("%s\n",text); -} - -int main( int argc, char* argv[] ) -{ - // initialize the heap check library - bool memDebugFl = cmDEBUG_FL; - unsigned memGuardByteCnt = memDebugFl ? 8 : 0; - unsigned memAlignByteCnt = 16; - unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0; - - cmCtx_t ctx; - cmCtxSetup(&ctx,"cm test",print,print,NULL,memGuardByteCnt,memAlignByteCnt,memFlags); - - cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &ctx.rpt ); - - cmFsInitialize( &ctx, "cm_test" ); - - cmTsInitialize(&ctx ); - - cmTsFinalize(); - cmFsFinalize(); - cmMdReport( kIgnoreNormalMmFl ); - cmMdFinalize(); - return 0; -}