GIDForums  

Go Back   GIDForums > Computer Programming Forums > FLTK Forum
User Name
Password
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
 
Thread Tools Search this Thread Rate Thread
  #1  
Old 14-Feb-2005, 15:18
cable_guy_67's Avatar
cable_guy_67 cable_guy_67 is offline
Senior Member
 
Join Date: Oct 2004
Location: Nescopeck, PA
Posts: 1,109
cable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the rough

GIMcontacts - a gim fltk fluid venture


GIMcontacts

a module using FLTK 1.1x/fluid and the GIM data handling back end
requires gim.h and associated files that can be found here


Goals
  • Good Sound Programming - Take the time to learn how the tools do their jobs. I believe it is this rule that slowed me down the most.
  • Completely Open - I think this is the core of GIM myself. The real idea here is to see if we can bring people together using some of the ideals from here at GIDForums. Provide, Practice, Patience and Promote. This whole place feels like a learning annex to me and a member of any level may be a teacher as well as student. My goal with this GIM module is to show a few things:
    • Show how to interact with gim.h
    • Show how to use fluid to build an entire app
    • Show how to use non-fluid defined Widgets while still using fluid to generate code
    • Explain what has to be done to access Class members from FLTK callbacks
    • Create a simple reusable Makefile that uses fluid as a command line compiler
  • A Learning Experience - Since each step to understanding is beset on all sides by the tyrany of obfuse shortcuts I am trying to do this in a way that will build in blocks instead of trying to get to the end of the race first. To start with GIMcontacts might not be the prettiest thing at the ball, but it should be easy to understand what is going on. I would like to build from this base as I learn better FLTK/C++ programming techniques.
  • Let there be FUN - I have to give dsmith credit. The best way I have found to learn new things is to have an interest in some kind of project that keeps you going. I have gotten stuck more times than I can count but have not felt like I was only working for the end result. It seems more like I have been winning tiny little battle after tiny battle (and savoring each) instead of worrying about the war. This was due in part to dsmith's providing of the console test program that gives a good basic understanding on how to interact with gim.h and the interface in general. I have used this as a basis for the GUI.

OK, with that in mind I envite you to take a stroll into using gim.h from a GUI built using FLTK and generating all the source code for the application from the Fluid UI builder currently included as part of the FLTK distribution.

Currently GIMcontacts is at version 0.1.0 and is considered the entry point for the community project source distribution. I will see about getting the package put up at the GIM homepage and possibly at a hosted open source site (my vote is still for Savanna) in the next few days. Included here will be the fluid project file, the makefile, the input cleaning routine file(which will be incorporated in the defined GCT_contact class) and have provided current copies of the needed files in the attached zip file. You will need to provide the environment to compile the source into a working executable. I use CygWin as my windows unix emulation and therefore use the GNU tools in this environment. FLTK is built from subversion sources following the 1.1.x branch. fltk.org has recently changed to using subversion instead of cvs and I have found this very nice to work with.

This time I am assuming that you can use fluid at least mechanically. There is a simple tutorial that can help if you just opened fluid for the first time that can be found here and an in-depth discussion on What is GIM (and what it is not) can be found here. The zipfile is actually a proposed directory structure for GIM and it's modules. I am using a base GIM directory that contains the module directory contacts. I do this so I can refer my include paths from the location of the makefile. Since the makefile is at the base of the module directory structure GIM is located at "../" and the needed fluid files are at "./fluid/*.fld". You get the picture. You don't have to do it that way but the makefile uses this idea so if you want to use the makefile you will have to use this directory structure or modify the makefile.

The rest of this initial post is just a general overview of what I have wrought and can be found in GIMcontacts.h

CPP / C++ / C Code:
/**********************************************************************/
/**********************************************************************/
/* The GIM project                                                    */
/**********************************************************************/
/* FILENAME:         GIMcontacts###.fld  - main fluid project file    */
/* FILENAME:         GIMcontacts###.cxx  - GIMcontacts main code file */
/* FILENAME:         GIMcontacts###.h    - GCT_contact class file     */
/* ORIGINATION DATE: Nov. 17, 2004                                    */
/* ORIGINATOR:       cable_guy_67                                     */
/* MODULE:           GIMcontacts                                      */
/* LICENSE:          gpl (gnu.org/copyleft/gpl.html)                  */
/**********************************************************************/
/* DESCRIPTION:                                                       */
/* Due to the nature of fluid this was the best I could manage to     */
/* include some basic information about this module.  It is a name,   */
/* address, number, etc manager that is simple to use with either the */
/* mouse or keyboard.  I don't know if there is a limit on the size   */
/* of the database file.  That would fall in the lap of dsmith, the   */
/* creator of the data portion of this project.  GIMcontacts is quite */
/* simple when you break it down.  The constructor builds the GUI and */
/* prepares it for showing and then sits and waits for user events    */
/* that call their associated callback.  There is a little bit of     */
/* trickery to get this all to work in fluid and still have the       */
/* callbacks be member functions of the main class.  This can be      */
/* seen in the static/inline callback pairs each of which define a    */
/* single callback.  This is so we could have access to our class     */
/* members from within a callback.  gim.h defines the base data       */
/* structure and this GUI is dependent on gim.h (the data back end)   */
/* and gct_data.cpp (the interface functions).  The rest is currently */
/* living in the GCT_contact class.                                   */
/**********************************************************************/
/* HISTORY:                                                           */
/*   0.0.1 - Start of contact prototyping.                            */
/*   0.0.2 - Initial definition of the GUI                            */
/*   0.0.3 - An attempt to see just how complex I could make this.    */
/*   0.0.4 - Initial move to using a single class for the GUI.        */
/*   0.0.5 - Working with gim.h structure.  Not all widgets defined.  */
/*   0.0.6 - Significant clarification of code flow                   */
/*   0.0.7 - Problem saving that is adding extra blank record         */
/*   0.0.8 - Fixed ? extra record by adding a ct_remove while loop    */
/*   0.0.9 - Cleaning / Documenting version for GID source release    */
/*   0.1.0 - Initial release for GIM community project.               */
/**********************************************************************/
This gives you a little background on the module itself and should be added to as GIMcontacts advances. Perhaps this would be a good place to include the needed class files. As it is, GIMcontacts is dependant on this info file since I can't (easily and realistically) add this at the top of the fluid generated .cxx and .h files.
CPP / C++ / C Code:
/**********************************************************************/
/* IMPORTANT REMINDERS:                                               */
/*  The first thing to do before anything is to make a call to        */
/*   ct_new() with the sizes of the structure member char*'s.  During */
/*  execution there will be ct_delete()/ct_new() pairs.  Be sure not  */
/*  to call ct_new() (after the initial call) without the call to     */
/*  ct_delete() first.                                                */
/*                                                                    */
/*  The callbacks that need access to members of GCT_contact have to  */
/*  be coded in pairs.  I use the format:                             */
/*                                                                    */
/*        static void cb_callbackname(Fl_Widget*, void *user){        */
/*                                                                    */
/*    Where you declare a fake this pointer:                          */
/*          GCT_contact *fake_this = (GCT_contact*)user;              */
/*          fake_this->icb_callbackname(); // the real callback       */
/*        }                                                           */
/*        inline void icb_callbackname(){                             */
/*          // the actual callback code with full access              */
/*          // to any class members it needs.                         */
/*        }                                                           */
/*    Because of this we set the callback in the constructor.         */
/*        menu_myitem->callback(cb_callbackname, this );              */
/*                                                                    */
/**********************************************************************/
This was the first big hurdle while converting to the class. Fluid makes it a little more difficult to pass the this pointer through the callback since it uses the user_data for it's own purpose. This can be gotten around in the method described above. The icb_name files really are just member functions of the GCT_contact class but are named to show their relationship to the cb_name function. Some of these icb_'s can probably be removed in favor of allowing the 'real' callback directly call one of the called member functions. This is part of my long division method of explanation. As they are streamlined changes will be noted here for documentation purposes.
CPP / C++ / C Code:
/**********************************************************************/
/*                                                                    */
/*   IMPORTANT VARIABLES:                                             */
/*     char gct_name[240] - this holds the name of the file and db    */
/*       and is used in some checking (against "unnamed").  We have   */
/*       been using ".gct" as an extention and should be removed      */
/*       from the display and put on a save file.                     */
/*     bool gct_modified - this is set when information is placed in  */
/*       the contact *current struct.  Used for decisions on saving   */
/*       to disk.                                                     */
/*     bool rec_modified - this is set when a change is made to the   */
/*       current record but has not been saved to *current yet. This  */
/*       is to save the write until changing pages for any reason.    */
/*     unsigned short int record_num - will one day show the current  */
/*       record record (after the sort) and will take a click to pop  */
/*       an input request to jump to a particular record.             */
/*     contact *current - This is our very own gim.h defined struct   */
/*       we will use for the life of the GUI.  This is accessed by    */
/*       using the functions defined in gim.h for the file            */
/*       gct_data.cpp (that includes the dlist files) and is the      */
/*       de-facto interface description.  These public functions are  */
/*       the only thing the GUI should "see".  Just remember, when    */
/*       you call a function with "ct_<name>()" it is acting on       */
/*       *current and cannot be touched until calling ct_new().       */
/**********************************************************************/
By using these four variables (and their related widgets) along with our contact *current structure we have all then information we need to track the life of the database and GUI. I have tried to localize the useage in a way that makes it easy to not forget when and how to react to user events. gct_name is the file we are working with and the gct_modified/rec_modified are the switches that get the database to follow along. I note any changes to the shown record with rec_modified and hold any writes to *current until that info is about to be lost. Then with a simple boolean check I can put the new information in the db. gct_modified tracks changes to the actual file that existis on your harddrive. When there is a change and you are going to change to a different db (or exit the program) GIMcontacts lets you know that you have made changes and asks if you want to save those changes. Simple stuff.
CPP / C++ / C Code:
/**********************************************************************/
/*                                                                    */
/*   IMPORTANT MEMBER FUNCTIONS:                                      */
/*     void check_mod() - this will check the status of gct_modified  */
/*       and prompt the user for a save name etc.  It is responsible  */
/*       for making sure gct_name is not NULL.  I should just add a   */
/*       check for this before going to save_contacts() to be safe.   */
/*     void save_contacts() - This need to have the save name stored  */
/*       in *gct_name before calling.  Upon a succesful save send the */
/*       value of *gct_name to out_filename and unset gct_modified.   */
/*       If the ct_save fails *gct_name should be set to the value    */
/*       in out_filename and the user is presented with their current */
/*       record.                                                      */
/*     void load_contacts() - this uses fl_file_chooser to get a      */
/*       file from the users filesystem and store the name in         */
/*       gct_name.  Then you must clean out *current with ct_delete() */
/*       and then ct_new() to get fresh lists.  I'm not sure if it    */
/*       would be better to call this (after the inital call in       */
/*       main() ) without parameters (does this use my params from    */
/*       the first call?) or with the same as initial call. There is  */
/*       no going back without reloading the previous *current and    */
/*       at present a load fail puts "loadfail" in *gct_name and      */
/*       out_filename.  Upon success *gct_name is put in out_filename */
/*       and a successful ct_home sets record_num to 1, put it in     */
/*       nav_view  set gct_modified to false and call display() to    */
/*       set the GUI to *current.                                     */
/*     void modify_contact() - this will set *current to the values   */
/*       in the GUI. It then unsets rec_modified and sets gct_modified*/
/*     void display() - this is the opposite of modify_contact() in   */
/*       that it sets the GUI to the *current record.  It should then */
/*       unset rec_modified.                                          */
/*     void group_hide() / void group_show() - oddly, this pair       */
/*       does just what you may think, hides or shows the groups      */
/*       and widgets that relate directly to the info in *current.    */
/*       I would like this to also resize the main window to reflect  */
/*       whether or not there are any records in *current.            */
/*                                                                    */
/*     The name callback was a test to check the slightly updated     */
/*     clean_input formatting routines.  They will be updated to      */
/*     correctly implement the text filters.                          */
/*                                                                    */
/**********************************************************************/
This is the list of member functions that you will be calling into from the callbacks. By trying to keep this down to a minimum I hope to make it easier to add the functionality that I want without getting bogged down with the list of individual callbacks. This is probably also where I should note when and which bool flags are being switched.
CPP / C++ / C Code:
/**********************************************************************/
/*                                                                    */
/*   GENERAL DISCLAIMER BY: cable guy                                 */
/*     This code is still growing into it's own functionality.  I     */
/*     am a hobbiest coder so please point out any obvious problems   */
/*     if you ponder this code.  It is my first attempt at a full     */
/*     usable application using Fluid and FLTK.  I am currently       */
/*     working with fltk-1.1 from subversion at fltk.org r-4038 and   */
/*     using gnu tools under cygwin running on W2K.  Working entirely */
/*     in fluid posed some challenges when I was prodded to move to   */
/*     the class version in front of you.  I believe that this should */
/*     be broken down further but I wanted to get it all together     */
/*     (maybe just to figure out how) before segmenting the code in   */
/*     smaller classes that work together.  Aside from taking care    */
/*     with NULL pointers and not calling ct_new() without ct_delete()*/
/*     I had very few .stackdumps to clean up.  FLTK seems to handle  */
/*     value limits very well so you get a little added safety by     */
/*     using the widget's value->() when going back and forth to the  */
/*     values in current->name_of.                                    */
/*                                                                    */
/**********************************************************************/
/**********************************************************************/
/*                                                                    */
/*   This is a direct result of the GIM community project that was    */
/*   first proposed by dsmith.  You can find the thread at            */
/*   GIDForums.com in the C/C++ tutorial/code sample sub-forum.  I    */
/*   can be found wandering around the FLTK forum as cable_guy_67.    */
/*                                                                    */
/**********************************************************************/
/**********************************************************************/

And that is really all there is to it. I hope this is helpful in showing how gim.h can remove just about all of the concern about working with the user data collected by the GUI. There is still much to be done as this is merely a 'working' (loosly used) example of a GIM client. I will follow up this post with links to related discussion threads as warrented. Please feel free to jump in with any input (pro/con/other) that could help improve this program. It is my belief that this can be significantly streamlined along the way. But for that I will need some input on the current state of affairs as I have been looking at the code too long and hard by myself. I will be documenting and adding the remaining things needed to get it to a full working version. Here is a short list of things that I think are next in line:
  • Turn the GCT_contact class into a base GIM Module class that handles the db.
  • Seperate the GUI into it's own class that is used by the base class. In that way changing the GUI for a different module should be easy to do.
  • Sub-class the widgets with special text formatting needs.
  • Write up the "About"s to tell a little about each.
  • Get the record tracking working correctly.
  • Make the file chooser a little less annoying (partly due to my not letting you out until the name contains something).
  • Create an install: dummy for the makefile to create the base dir structure.

The Makefile

This came about due to my lazyness. Working with fluid can be trying enough so I didn't want to hassle with the individual files every time I made a change. Compile times(while not terribly long) were starting to get longer and longer so I turned to GNU make to help out. I tried to stay away from any make syntax trickery and added a whole list of variables to make it easy to use this for any GIM dependent product. The compiler and linker lines needed can be gotten by calling:
Code:
fltk-config --cflags --cxxflags --ldflags
and copying the returned values to the makefile. I also am assuming that you can "see" the commands your shell will need. Any suggestions are appreciated here as my ability to work with make seems to come and go. I think I just need more practice. Each of the individual dependency code files can be called with the dummy holding their name. In case you don't know, the dummy targets I am referring to are sort of like label jumps or goto's to circumvent the normal operation. These are useful for things like doing an install, creating an individual object (.o) file and cleaning up. If you just want to generate the .exe and don't care about all that just go to the gim/contacts directory and execute make from the command line. Once you have the makefile set to your needs it will keep track of what needs to be recompiled and then link the up-to-date objects for you. Now when working in fluid all you need to do is save your project from the fluid project window, switch to your console and type make. Once you are done with fluid you can modify the .cxx and .h files to your hearts content and as long as you don't touch the fluid project, your work will be safe. To do this, I had to leave out the fluid projects dependencies on gim.h since that would cause a rebuild of your .cxx and .h files effectively destroying any changes. Maybe there is a better solution to this but this does work as anticipated. Basically just keep these in mind:
  • make - rebuilds anything needed and link the final .exe show the size and run the app
  • make clean - calls mostly_clean and then deletes the .exe
  • make mostly_clean - deletes the object files
  • make gim/gct_data/gct/contacts/fluid - touches the file to force a rebuild of it.
  • make run - will run your .exe (talk about lazy )

Well, that is about it. Please jump in with anything good/bad/ugly that could help this program to the next stage. I am enjoying working with fltk as I never would have thought you get get so much power with so little effort. My hat goes off to the Fast Light Tool Kit Developers.
Attached Files
File Type: zip GIMcontacts010_src.zip (89.5 KB, 44 views)
__________________
"Opportunity is missed by most people because it comes dressed in overalls and looks like work."
--Thomas Alva Edison
"Those who would give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety."
--Benjamin Franklin
"A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes."
--Hugh Downs
 
 

Recent GIDBlogOnce again, no time for hobbies by crystalattice

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
run() and fluid Acki FLTK Forum 9 01-Oct-2008 19:38
FLTK && fluid In Motion cable_guy_67 FLTK Forum 4 20-Mar-2008 04:52
[Tutorial] GUI programming with FLTK dsmith FLTK Forum 10 03-Oct-2005 16:41
Any disadvantages with FLUID? r2matthews FLTK Forum 4 14-Nov-2004 18:49
Welcome to the FLTK Forums dsmith FLTK Forum 0 08-Sep-2004 07:58

Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The

All times are GMT -6. The time now is 22:23.


vBulletin, Copyright © 2000 - 2009, Jelsoft Enterprises Ltd.