diff --git a/CHANGELOG b/CHANGELOG index 571227b..9f016d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,4 @@ -Planned for version 1.0: -- Make sub-map list scrollable -- Implement line style (and/or width?) function -- Implement delete current map function + Ideas for future versions: - Fix the bug where netmapr crashes on my 24-bit Truecolour display under @@ -9,12 +6,22 @@ Ideas for future versions: - Export diagrams to XML - Export diagrams to some format which Visio can import - Perhaps change to SVG for objects -- Add more objects (eg. broadband router, VPN concentrator, etc) +- Add more objects (eg. broadband router, etc) - Snap-to-grid feature - Make size+position matching work for text (currently only works on objects) - Implement a toggle-able traffic flow display (for example, you might click a button and have arrows appear to show all SMTP mail flows) -- Ability to select multiple items at once + +Version 1.1: +- Added rollover help text and highlights +- Added object: VPN concentrator +- Added dual-head arrows on lines + +Version 1.0: +- Sub-map list is now scrollable +- Implemented line style, width and arrowhead functions. +- Implemented delete current map function +- Added objects: access point, pda, laptop Version 0.99d: - Added readonly mode if argv[0] contains "view" diff --git a/constants.h b/constants.h index ad6ad2b..7e298ca 100644 --- a/constants.h +++ b/constants.h @@ -1,4 +1,4 @@ -#define VERSION "1.0" +#define VERSION "1.1" #define BUFLEN 512 @@ -47,9 +47,6 @@ #define LINESELHANDLESIZE (5) #define OBJSELHANDLEPCT (15) -/* arrow positions */ -#define AP_START (1) -#define AP_END (2) /* used as a NULL value for colours */ #define NOCOLOUR (99) @@ -62,6 +59,7 @@ #define T_LINK (2) #define T_TEXT (3) #define T_LINKPOINT (4) +#define T_MAP (5) #define O_ROUTER (0) #define O_SWITCH (1) @@ -141,12 +139,20 @@ #define MAXLINESTYLE (4) #define LINESTYLESIZE (10) -#define LA_NONE (0) -#define LA_SOURCE (1) -#define LA_DEST (2) -#define LA_BOTH (3) +/* arrow positions */ +#define AP_NONE (0) +#define AP_START (1) +#define AP_END (2) +#define AP_BOTH (3) #define C_NONE (-1) #define C_SCROLLUP (-2) #define C_SCROLLDOWN (-3) +/* mouse positions */ +#define MP_NONE (0) +#define MP_TOOLBOX (1) +#define MP_MAPBOXNAME (2) +#define MP_MAPBOXCHILDREN (3) +#define MP_OBJECTBOX (4) + diff --git a/dist.sh b/dist.sh index 8829177..c6ca9ee 100755 --- a/dist.sh +++ b/dist.sh @@ -6,12 +6,12 @@ TARFILE=${FULLNAME}.tar.gz WINFILE=${FULLNAME}-win32.zip mkdir ${FULLNAME} -cp README INSTALL Makefile.linux Makefile.freebsd Makefile.windows objects.dat buttons.dat netmapr.c netmapr.h constants.h convert.c convert.h icon.bmp verdana.ttf example.map ${FULLNAME}/ +cp README.txt INSTALL.txt Makefile.linux Makefile.freebsd Makefile.windows objects.dat buttons.dat netmapr.c netmapr.h constants.h convert.c convert.h icon.bmp verdana.ttf example.map ${FULLNAME}/ tar zcvf ${TARFILE} ${FULLNAME} rm -rf ${FULLNAME} mkdir ${FULLNAME} -cp README INSTALL objects.dat buttons.dat netmapr.exe netmapr.c netmapr.h constants.h convert.c convert.h verdana.ttf example.map icon.bmp windows_files/*.dll ${FULLNAME}/ +cp README.txt INSTALL.txt objects.dat buttons.dat netmapr.exe netmapr.c netmapr.h constants.h convert.c convert.h verdana.ttf example.map icon.bmp windows_files/*.dll ${FULLNAME}/ zip -r ${WINFILE} ${FULLNAME} rm -rf ${FULLNAME} diff --git a/icon.bmp b/icon.bmp index 70316ea..ee45543 100644 Binary files a/icon.bmp and b/icon.bmp differ diff --git a/netmapr.c b/netmapr.c index 397d109..9a2206a 100644 --- a/netmapr.c +++ b/netmapr.c @@ -55,6 +55,10 @@ int modified = FALSE; int readonly = FALSE; +int copyfrom = -1; +int copymap = -1; +int copytype = T_MAP; + int numobjtypes = 0; int numletters = 0; int numbuttons = 0; @@ -76,6 +80,15 @@ int bgw,bgh; int xoff,yoff; SDL_Rect temparea; char statustext[BUFLEN]; +char oldstatustext[BUFLEN]; +int rollover = FALSE; +int oldmousepos = -1; +int mousepos = 0; +int oldselection = -1; +int dontpaste = FALSE; + +SDL_Surface *toolbg; +SDL_Surface *toolhilite; int curmap = 0; @@ -130,6 +143,8 @@ int main (int argc, char **argv) { } atexit(cleanup); + strcpy(oldstatustext,""); + if (autoload) { loadmap(); strcpy(text, ""); @@ -610,6 +625,12 @@ int main (int argc, char **argv) { changestate(S_ADDTEXT); break; case TB_NEW: + tmod = SDL_GetModState(); + if (!(mod & KMOD_SHIFT)) { + sprintf(statustext, "Must hold down SHIFT to start new project (avoids accidental loss of data)!"); + drawstatusbar(); + break; + } initvars(); changestate(S_NONE); drawmap(); @@ -709,10 +730,10 @@ int main (int argc, char **argv) { drawmap(); break; case TB_DELETEMAP: - /* safety - must hold down ALT to delete maps */ + /* safety - must hold down SHIFT to delete maps */ tmod = SDL_GetModState(); if (!(mod & KMOD_SHIFT)) { - sprintf(statustext, "Must hold down SHIFT to delete maps (avoids accidental deletion)!"); + sprintf(statustext, "Must hold down SHIFT to delete maps (avoids accidental loss of data)!"); drawstatusbar(); break; } @@ -907,6 +928,190 @@ int main (int argc, char **argv) { case S_LINKPOINTMOVE: updatelinkpointshadow(event.button.x, event.button.y); break; + case S_NONE: + /* has mouse moved since last time? */ + mousepos = getmousepos(event.button.x, event.button.y); + if ((mousepos != oldmousepos ) || (mousepos== MP_TOOLBOX)) { + /* rollovers */ + if (mousepos == MP_TOOLBOX) { + SDL_Rect area; + int tempx,tempy, selection; + int boxy; + + if (!rollover) strcpy(oldstatustext, statustext); + rollover = TRUE; + + /* determine which button we're over */ + tempx = (event.button.x - toolbox.x) / (toolbox.gridsize+3); + tempy = (event.button.y - toolbox.y) / (toolbox.gridsize+3); + + boxy = toolbox.y + (tempy*(toolbox.gridsize+3)); + + selection = tempy*toolbox.gridrowlen + tempx; + + /* replace old button */ + if (toolbg != NULL) { + SDL_Rect area; + + + if (oldselection >= 0) { + if (dontpaste == FALSE) { + area.x = toolbox.x + ((oldselection % 3 )*(toolbox.gridsize+3)); + area.y = toolbox.y + ((oldselection / 3)*(toolbox.gridsize+3)); + area.w = toolbox.gridsize; + area.h = toolbox.gridsize; + SDL_BlitSurface(toolbg, NULL, screen, &area); + SDL_UpdateRect(screen, area.x, area.y, area.w, area.h); + } else { + dontpaste = FALSE; + } + } + SDL_FreeSurface(toolbg); + toolbg = NULL; + } + /* copy background of this button */ + if (selection <= TB_SAVE) { + toolbg = SDL_CreateRGBSurface(SDL_SWSURFACE,toolbox.gridsize+3, toolbox.gridsize+3, + screen->format->BitsPerPixel, screen->format->Rmask, + screen->format->Gmask,screen->format->Bmask, + screen->format->Amask); + area.x = toolbox.x + ((selection % 3 )*(toolbox.gridsize+3)); + area.y = toolbox.y + ((selection / 3)*(toolbox.gridsize+3)); + area.w = toolbox.gridsize+1; + area.h = toolbox.gridsize+1; + SDL_BlitSurface(screen,&area, toolbg, 0); + + /* draw highlight */ + if (selection == TB_LINESTYLE) { + SDL_Rect sarea; + + if (event.button.y <= (boxy + 9)) { + sarea.x = 0; + sarea.y = 0; + sarea.w = toolbox.gridsize; + sarea.h = 11; + area.h = 11; + area.y += 1; + } else if (event.button.y <= (boxy + 19)) { + sarea.x = 0; + sarea.y = 0; + sarea.w = toolbox.gridsize; + sarea.h = 10; + area.h = 10; + area.y += 12; + } else { + sarea.x = 0; + sarea.y = 0; + sarea.w = toolbox.gridsize; + sarea.h = 8; + area.h = 8; + area.y += 21; + } + SDL_BlitSurface(toolhilite, &sarea, screen, &area); + } else { + SDL_BlitSurface(toolhilite, NULL, screen, &area); + } + + SDL_UpdateRect(screen, area.x, area.y, area.w, area.h); + } + + if (selection != oldselection) { + switch (selection) { + case TB_POINTER: + sprintf(statustext, "LMB = Enter object selection mode"); + drawstatusbar(); + break; + case TB_ADDOBJ: + sprintf(statustext, "LMB = Add an object to the map"); + drawstatusbar(); + break; + case TB_ADDTEXT: + sprintf(statustext, "LMB (on map) = Add text to the map, LMB (on object) = Add text to an object"); + drawstatusbar(); + break; + case TB_FGCOL: + sprintf(statustext, "LMB = Apply default foreground colour to current selection, RMB = Change default foreground colour"); + drawstatusbar(); + break; + case TB_FILLCOL: + sprintf(statustext, "LMB = Apply default fill colour to current selection, RMB = Change default fill colour"); + drawstatusbar(); + break; + case TB_LINESTYLE: + if (event.button.y <= (boxy + 9)) { + sprintf(statustext, "LMB = Apply default thickness to current line, RMB = Change default line thickness"); + } else if (event.button.y <= (boxy + 19)) { + sprintf(statustext, "LMB = Apply default style to current line, RMB = Change default line style"); + } else { + sprintf(statustext, "LMB = Apply default arrowhead type to current line, RMB = Change default arrowhead type"); + } + drawstatusbar(); + break; + case TB_MATCHSIZE: + sprintf(statustext, "LMB = Match size of current selection to next object clicked"); + drawstatusbar(); + break; + case TB_MATCHX: + sprintf(statustext, "LMB = Match horizontal position of currently selected object to next object clicked"); + drawstatusbar(); + break; + case TB_MATCHY: + sprintf(statustext, "LMB = Match vertical position of currently selected object to next object clicked"); + drawstatusbar(); + break; + case TB_DRILLDOWN: + sprintf(statustext, "LMB = Drill down to create new map under current object"); + drawstatusbar(); + break; + case TB_CREATETELE: + sprintf(statustext, "LMB = Set destination map for when current object is double-clicked"); + drawstatusbar(); + break; + case TB_DELETEMAP: + sprintf(statustext, "LMB = Delete current map (hold down SHIFT to confirm)"); + drawstatusbar(); + break; + case TB_NEW: + sprintf(statustext, "LMB = Delete ALL maps and start a fresh project (hold SHIFT to confirm)"); + drawstatusbar(); + break; + case TB_LOAD: + sprintf(statustext, "LMB = Load a new map"); + drawstatusbar(); + break; + case TB_SAVE: + sprintf(statustext, "LMB = Save the current map to disk (use a .BMP extension to export to bitmap format)"); + drawstatusbar(); + break; + default: + break; + } + } + oldselection = selection; + } else if (mousepos == MP_MAPBOXNAME) { + if (!rollover) strcpy(oldstatustext, statustext); + rollover = TRUE; + sprintf(statustext, "LMB = Change the name of the current map."); + drawstatusbar(); + } else if (mousepos == MP_MAPBOXCHILDREN) { + if (!rollover) strcpy(oldstatustext, statustext); + rollover = TRUE; + sprintf(statustext, "LMB = Drill down into a submap."); + drawstatusbar(); + } else if (mousepos == MP_OBJECTBOX) { + if (!rollover) strcpy(oldstatustext, statustext); + rollover = TRUE; + sprintf(statustext, "LMB = Select a new object type."); + drawstatusbar(); + } else { + strcpy(statustext, oldstatustext); + strcpy(oldstatustext, ""); + drawstatusbar(); + rollover = FALSE; + } + } + oldmousepos = mousepos; + break; } break; @@ -1088,6 +1293,30 @@ int main (int argc, char **argv) { } } } + if (c == 'c') { /* copy */ + if (map[curmap].selecteditem == -1) { + /* copy entire map */ + copytype = T_MAP; + copyfrom = -1; + copymap = curmap; + sprintf(statustext,"Map %d ('%s') set as copy source.",curmap, map[curmap].name); + drawstatusbar(); + } else { + if (map[curmap].selecteditemtype == T_OBJECT) { + copytype = T_OBJECT; + copymap = curmap; + copyfrom = map[curmap].selecteditem; + sprintf(statustext,"Object %d set as copy source.",copyfrom); + drawstatusbar(); + } else if (map[curmap].selecteditemtype == T_TEXT) { + copytype = T_TEXT; + copymap = curmap; + copyfrom = map[curmap].selecteditem; + sprintf(statustext,"Text %d ('%s') set as copy source.",copyfrom, map[curmap].textob[copyfrom].text); + drawstatusbar(); + } + } + } if (c == 'd') { /* drill down */ if (state == S_NONE) { if ((map[curmap].selecteditemtype == T_OBJECT) && (map[curmap].selecteditem != -1)) { @@ -1101,6 +1330,73 @@ int main (int argc, char **argv) { drawstatusbar(); } } + if (c == 'p') { /* paste */ + if (copytype == T_MAP) { + if (copymap == curmap) { + sprintf(statustext,"Error: copy source and destination are the same!"); + drawstatusbar(); + } else if (copymap < 0) { + sprintf(statustext,"Error: No copy source selected!"); + drawstatusbar(); + } else { + map[curmap] = map[copymap]; + sprintf(map[curmap].name, "Copy of map%d",copymap); + modified = TRUE; + sprintf(statustext,"Map %d ('%s') copied to map %d.", copymap, map[copymap].name, curmap); + copytype = -1; + copymap = -1; + drawmap(); + + } + } else if (copytype == T_TEXT) { + int tnum; + /* create new text object */ + if ((map[copymap].textob[copyfrom].x + 10) >= map[curmap].width) { + startx = map[copymap].textob[copyfrom].x - 10; + } else { + startx = map[copymap].textob[copyfrom].x + 10; + } + if ((map[copymap].textob[copyfrom].y + 10) >= map[curmap].height) { + starty = map[copymap].textob[copyfrom].y - 10; + } else { + starty = map[copymap].textob[copyfrom].y + 10; + } + if (copymap == curmap) { + textanchor = map[copymap].textob[copyfrom].anchor; + if (textanchor != -1) { + startx += map[copymap].obj[textanchor].x; + starty += map[copymap].obj[textanchor].y; + } + } else { + textanchor = -1; + } + strcpy(text, map[copymap].textob[copyfrom].text); + + /* create it */ + endtext(); + + sprintf(statustext,"Text object pasted at %d,%d.",startx,starty); + + /* update its size */ + tnum = map[curmap].numtext-1; + map[curmap].textob[tnum].h = map[copymap].textob[copyfrom].h; + map[curmap].textob[tnum].w = map[copymap].textob[copyfrom].w; + map[curmap].textob[tnum].c = map[copymap].textob[copyfrom].c; + + /* select new item */ + map[curmap].selecteditemtype = T_TEXT; + map[curmap].selecteditem = tnum; + + /* clear buffer */ + copytype = -1; + copymap = -1; + + drawmap(); + } else { + sprintf(statustext,"Error: No copy source selected!"); + drawstatusbar(); + } + } if (c == SDLK_BACKSPACE) { goback(); } @@ -1193,13 +1489,13 @@ int addvector(vectorimg_t *vimg, int type, int x1, int y1, int x2, int y2, SDL_C void changelinearrow(int changeby) { if ((changeby < 0) && (defarrow == 0)) { - defarrow = AP_END; + defarrow = AP_BOTH; } else { defarrow += changeby; } /* wrap around */ - if (defarrow > AP_END) defarrow = 0; + if (defarrow > AP_BOTH) defarrow = 0; /* change arrow style on currently selected line */ if (map[curmap].selecteditemtype == T_LINK) { @@ -2022,7 +2318,12 @@ void drawline(SDL_Surface *screen, int x1, int y1, int x2, int y2, SDL_Color c, /* draw arrowheads if required */ if (arrow) { - drawarrowhead(screen, x1, y1, x2, y2, c, linestyle, arrow); + if ((arrow == AP_START) || (arrow == AP_END)) { + drawarrowhead(screen, x1, y1, x2, y2, c, linestyle, arrow); + } else if (arrow == AP_BOTH) { + drawarrowhead(screen, x1, y1, x2, y2, c, linestyle, AP_START); + drawarrowhead(screen, x1, y1, x2, y2, c, linestyle, AP_END); + } } } @@ -2867,6 +3168,8 @@ void drawtoolbox(void) { SDL_Color gridmiddle = {90, 90, 90, 0}; SDL_Color gridlow = {50, 50, 50, 0}; + dontpaste = TRUE; + fillcol = SDL_MapRGB(screen->format, toolbox.bgcol.r,toolbox.bgcol.g,toolbox.bgcol.b); area.x = toolbox.x; area.y = toolbox.y; @@ -3059,6 +3362,9 @@ int endtext(void) { } if (strlen(text) == 0) { + sprintf(statustext,"Text entry aborted."); + changestate(S_NONE); + drawmap(); return TRUE; } @@ -3086,8 +3392,9 @@ int endtext(void) { map[curmap].thing[map[curmap].numthings].type = T_TEXT; map[curmap].thing[map[curmap].numthings].id = map[curmap].numtext; + modified = TRUE; - sprintf(statustext,"Added text object #%d: '%s'\n",map[curmap].numtext, map[curmap].textob[map[curmap].numtext].text); + sprintf(statustext,"Added text object #%d: '%s'.",map[curmap].numtext, map[curmap].textob[map[curmap].numtext].text); drawstatusbar(); map[curmap].numtext++; @@ -3254,6 +3561,22 @@ void floodfill4(SDL_Surface *dest, int x, int y, SDL_Color fillcol, SDL_Color bg } } +int getmousepos (int x,int y) { + if (isontoolbox(x, y)) { + return MP_TOOLBOX; + } else if (isonmapbox(x,y)) { + if (isonmapname(x,y)) { + return MP_MAPBOXNAME; + } else { + return MP_MAPBOXCHILDREN; + } + } else if (isonobox(x,y)) { + return MP_OBJECTBOX; + } + + return MP_NONE; +} + int getcolor(SDL_Surface *dest, int x, int y, SDL_Color *col) { Uint32 *pixel; int bpp; @@ -3715,6 +4038,8 @@ int initgraphics(void) { int i; char verstring[BUFLEN]; char file[BUFLEN]; + Uint32 fillcol; + SDL_Rect area; int x1,x2,y1,y2; SDL_Color c; @@ -3727,6 +4052,8 @@ int initgraphics(void) { initvars(); + + /* set up icon */ sprintf(file, "icon.bmp"); icon = SDL_LoadBMP(file); @@ -3888,6 +4215,21 @@ int initgraphics(void) { SDL_WM_SetCaption(verstring,"netmapr"); } + + /* set up toolbox highlight image */ + toolhilite = SDL_CreateRGBSurface(SDL_SWSURFACE,toolbox.gridsize+3, toolbox.gridsize+3, + screen->format->BitsPerPixel, screen->format->Rmask, + screen->format->Gmask,screen->format->Bmask, +screen->format->Amask); + fillcol = SDL_MapRGB(screen->format, 250, 250, 250); + + area.x = 0; + area.y = 0; + area.w = toolbox.gridsize+1; + area.h = toolbox.gridsize+1; + SDL_FillRect(toolhilite, &area, fillcol); + SDL_SetAlpha(toolhilite, SDL_SRCALPHA, 128); + /* initialise buffer */ buffer = SDL_CreateRGBSurface(SDL_SWSURFACE,map[curmap].width,map[curmap].height, screen->format->BitsPerPixel, screen->format->Rmask, diff --git a/netmapr.exe b/netmapr.exe index ee68285..058fd02 100755 Binary files a/netmapr.exe and b/netmapr.exe differ diff --git a/netmapr.h b/netmapr.h index 1a7de22..b7286c4 100644 --- a/netmapr.h +++ b/netmapr.h @@ -213,6 +213,7 @@ void floodfill2(SDL_Surface *dest, int x, int y, SDL_Color fillcol, SDL_Color bg void floodfill3(SDL_Surface *dest, int x1, int x2, int y, SDL_Color fillcol, SDL_Color bgcol); void floodfill4(SDL_Surface *dest, int x, int y, SDL_Color fillcol, SDL_Color bgcol); int getcolor(SDL_Surface *dest, int x, int y, SDL_Color *col); +int getmousepos(int x, int y); void drawyn(char *prompt); int getyn(int x, int y); void goback(void); diff --git a/objects.dat b/objects.dat index 8fbad17..3cabce9 100644 --- a/objects.dat +++ b/objects.dat @@ -462,13 +462,19 @@ line 134 117 134 39 0 255 255 line 118 50 134 39 0 255 255 fill 125 110 0 90 128 end -object redbox 120 120 +object box 120 120 box 0 0 119 119 0 0 0 fill 50 50 255 0 0 end -object greenbox 120 120 +object greybox 120 120 box 0 0 119 119 0 0 0 -fill 50 50 0 255 0 +fill 50 50 150 150 150 +#line 0 60 5 30 90 90 90 +#line 5 30 15 15 90 90 90 +#line 15 15 30 5 90 90 90 90 +#line 5 30 15 15 90 90 90 +#line 15 15 30 5 90 90 90 +#line 30 5 60 0 90 90 90 end object bluebox 120 120 box 0 0 119 119 0 0 0 @@ -593,3 +599,18 @@ line 300 249 399 190 170 230 255 line 399 150 399 190 170 230 255 fill 350 200 0 90 128 end +object vpnconcentrator 400 400 +# front +box 0 50 300 399 141 203 240 +# top +line 0 50 100 0 141 203 240 +line 100 0 399 0 141 203 240 +line 399 0 300 50 141 203 240 +# side (starts at bottom right of front) +line 300 399 399 350 141 203 240 +line 399 350 399 0 141 203 240 +# top tube +# centre is 350,175 xrad 10 yrad 25 +# start from bottom, go clockwise +#line 375 175 +end