diff options
Diffstat (limited to 'app/wlib')
49 files changed, 3495 insertions, 1274 deletions
| diff --git a/app/wlib/gtklib/CMakeLists.txt b/app/wlib/gtklib/CMakeLists.txt index bf20e91..97ab56f 100644 --- a/app/wlib/gtklib/CMakeLists.txt +++ b/app/wlib/gtklib/CMakeLists.txt @@ -18,6 +18,7 @@ set(sources  	menu.c	  	message.c  	notice.c +	opendocument.c  	pixbuf.c  	png.c  	print.c @@ -35,29 +36,43 @@ set(sources  	gtkdraw-cairo.c  	) -  # help system is OS and build specific, add appropriate source files  if(APPLE) -    set(sources  -        ${sources} -	osxhelp.c)	 -else() +   if (XTRKCAD_USE_APPLEHELP)  +   		set(sources  +      	  ${sources} +			osxhelp.c)	 +	else(XTRKCAD_USE_APPLEHELP) +		if(XTRKCAD_USE_BROWSER)  +        	set(sources  +          	  ${sources} +          	  browserhelp.c) +    	else(XTRKCAD_USE_BROWSER) +    	 PKG_CHECK_MODULES(GTK_WEBKIT "webkit-1.0" REQUIRED) +       	 set(sources  +       	     ${sources} +       	     ixhelp.c) +       	endif(XTRKCAD_USE_BROWSER) +    endif(XTRKCAD_USE_APPLEHELP)     +else(APPLE)      if(XTRKCAD_USE_BROWSER)           set(sources               ${sources}              browserhelp.c) -    else() +    else(XTRKCAD_USE_BROWSER) +    	PKG_CHECK_MODULES(GTK_WEBKIT "webkit-1.0" REQUIRED)          set(sources               ${sources}              ixhelp.c) -    endif()     -endif() +    endif(XTRKCAD_USE_BROWSER)     +endif(APPLE)  include_directories(${XTrkCAD_BINARY_DIR})  add_library(xtrkcad-wlib ${headers} ${sources})  # GTK  +find_package (GTK2)  include_directories(${GTK_INCLUDE_DIRS})  target_link_libraries(xtrkcad-wlib ${GTK_LIBRARIES}) @@ -67,7 +82,16 @@ include_directories(${GTK_UNIX_PRINT_INCLUDE_DIRS})  target_link_libraries(xtrkcad-wlib ${GTK_UNIX_PRINT_LIBRARIES})  # add dependency to webkit if configured -if(NOT XTRKCAD_USE_BROWSER) -    include_directories(${GTK_WEBKIT_INCLUDE_DIRS}) -    target_link_libraries(xtrkcad-wlib ${GTK_WEBKIT_LIBRARIES}) -endif() +if (APPLE) +	if(NOT XTRKCAD_USE_APPLEHELP)  +		if(NOT XTRKCAD_USE_BROWSER) +		   include_directories(${GTK_WEBKIT_INCLUDE_DIRS}) +	 	   target_link_libraries(xtrkcad-wlib ${GTK_WEBKIT_LIBRARIES}) +		endif() +	endif()  +else (APPLE) +	if(NOT XTRKCAD_USE_BROWSER) + 	   include_directories(${GTK_WEBKIT_INCLUDE_DIRS}) + 	   target_link_libraries(xtrkcad-wlib ${GTK_WEBKIT_LIBRARIES}) +	endif() +endif(APPLE) diff --git a/app/wlib/gtklib/browserhelp.c b/app/wlib/gtklib/browserhelp.c index 9351e86..aa8f5c7 100644 --- a/app/wlib/gtklib/browserhelp.c +++ b/app/wlib/gtklib/browserhelp.c @@ -22,12 +22,17 @@  #include <stdlib.h>  #include <assert.h> +#include <string.h> + +#include "misc.h"  #include "gtkint.h"  #include "i18n.h"  #include "dynstring.h" +#define debug 0 +  #define DEFAULTBROWSERCOMMAND "xdg-open"  #define  HELPERRORTEXT 			"Help Error - help information can not be found.\n" \ @@ -38,7 +43,7 @@  								"variable.\n Also make sure that the user has sufficient access rights to read these" \   								"files."  /** - * Create a fully qualified url froma topic + * Create a fully qualified url from a topic   *   * \param helpUrl OUT pointer to url, free by caller   * \param topic IN the help topic @@ -63,36 +68,6 @@ TopicToUrl(char **helpUrl, const char *topic)      DynStringFree(&url);  }  /** - * Extend the PATH variable inthe environment to include XTrackCAD's - * script directory. - * - * \return pointer to old path - */ - -static char * -ExtendPath(void) -{ -    char *path = getenv("PATH"); -    DynString newPath; -    DynStringMalloc(&newPath, 16); - -    // append XTrackCAD's directory to the path as a fallback -    DynStringCatCStrs(&newPath, -                      path, -                      ":", -                      wGetAppLibDir(), -                      NULL); - -    setenv("PATH", -           DynStringToCStr(&newPath), -           TRUE); - -    DynStringFree(&newPath); - -    return (path); -} - -/**   * Invoke the system's default browser to display help for <topic>. First the   * system's standard xdg-open command is attempted. If that is not available, the   * version included with the XTrackCAD installation is executed. @@ -104,34 +79,20 @@ void wHelp(const char * topic)  {      int rc;      char *url; -    DynString commandLine;      char *currentPath;      assert(topic != NULL);      assert(strlen(topic)); -    currentPath = ExtendPath(); -    TopicToUrl(&url, topic); +    if (!CheckHelpTopicExists(topic)) return; -    DynStringMalloc(&commandLine, 16); -    DynStringCatCStrs(&commandLine, -                      DEFAULTBROWSERCOMMAND, -                      " ", -                      url, -                      NULL); +    TopicToUrl(&url, topic); -    // the command should be found via the PATH -    rc = system(DynStringToCStr(&commandLine)); +	rc = wOpenFileExternal(url); -    if (rc) { +	if (!rc) {          wNotice(HELPERRORTEXT, _("Cancel"), NULL);      } -    // restore the PATH -    setenv("PATH", -           currentPath, -           TRUE); -      free(url); -    DynStringFree(&commandLine);  } diff --git a/app/wlib/gtklib/button.c b/app/wlib/gtklib/button.c index b5fabe8..51106c8 100644 --- a/app/wlib/gtklib/button.c +++ b/app/wlib/gtklib/button.c @@ -94,15 +94,23 @@ void wlibSetLabel(              } else {                  pixbuf = wlibPixbufFromXBM( bm );              } +            double scaleicon; +            wPrefGetFloat(PREFSECTION, LARGEICON, &scaleicon, 1.0); +            if (scaleicon<1.0) scaleicon=1.0; +            if (scaleicon>2.0) scaleicon=2.0; +            GdkPixbuf *pixbuf2 = +            		gdk_pixbuf_scale_simple(pixbuf, gdk_pixbuf_get_width(pixbuf)*scaleicon, gdk_pixbuf_get_height(pixbuf)*scaleicon, GDK_INTERP_BILINEAR); +            g_object_ref_sink(pixbuf); +            g_object_unref((gpointer)pixbuf);              if (*imageG==NULL) { -                *imageG = gtk_image_new_from_pixbuf(pixbuf); +                *imageG = gtk_image_new_from_pixbuf(pixbuf2);                  gtk_container_add(GTK_CONTAINER(widget), *imageG);                  gtk_widget_show(*imageG);              } else { -                gtk_image_set_from_pixbuf(GTK_IMAGE(*imageG), pixbuf); +                gtk_image_set_from_pixbuf(GTK_IMAGE(*imageG), pixbuf2);              } -            g_object_ref_sink(pixbuf); -            g_object_unref((gpointer)pixbuf); +            g_object_ref_sink(pixbuf2); +            g_object_unref((gpointer)pixbuf2);          } else {              if (*labelG==NULL) {                  *labelG = (GtkLabel*)gtk_label_new(wlibConvertInput(labelStr)); @@ -172,6 +180,18 @@ static void pushButt(  }  /** + * Called after expose event default hander - allows the button to be outlined + */ +static wBool_t exposeButt( +		GtkWidget *widget, +		GdkEventExpose *event, +		gpointer g) +{ +	wControl_p b = (wControl_p)g; +	return wControlExpose(widget,event,b); +} + +/**   * Create a button   *   * \param parent IN parent window @@ -198,7 +218,10 @@ wButton_p wButtonCreate(      void 	* data)  {      wButton_p b; -    b = wlibAlloc(parent, B_BUTTON, x, y, labelStr, sizeof *b, data); +    if (option&BO_ICON)  //The labelStr here is a wIcon_p +    	b = wlibAlloc(parent, B_BUTTON, x, y, " ", sizeof *b, data); +    else +    	b = wlibAlloc(parent, B_BUTTON, x, y, labelStr, sizeof *b, data);      b->option = option;      b->action = action;      wlibComputePos((wControl_p)b); @@ -206,9 +229,12 @@ wButton_p wButtonCreate(      b->widget = gtk_toggle_button_new();      g_signal_connect(GTK_OBJECT(b->widget), "clicked",                           G_CALLBACK(pushButt), b); +    g_signal_connect_after(GTK_OBJECT(b->widget), "expose-event", +    					G_CALLBACK(exposeButt), b);      if (width > 0) {          gtk_widget_set_size_request(b->widget, width, -1);      } +      if( labelStr ){          wButtonSetLabel(b, labelStr);      } @@ -484,6 +510,8 @@ wChoice_p wRadioCreate(      b->valueP = valueP;      wlibComputePos((wControl_p)b); +    ((wControl_p)b)->outline = FALSE; +      if (option&BC_HORZ) {          b->widget = gtk_hbox_new(FALSE, 0);      } else { @@ -506,6 +534,8 @@ wChoice_p wRadioCreate(          gtk_widget_show(butt);          g_signal_connect(GTK_OBJECT(butt), "toggled",                           G_CALLBACK(pushChoice), b); +        g_signal_connect_after(GTK_OBJECT(b->widget), "expose-event", +            					G_CALLBACK(exposeButt), b);          wlibAddHelpString(butt, helpStr);      } @@ -586,6 +616,8 @@ wChoice_p wToggleCreate(      b->action = action;      wlibComputePos((wControl_p)b); +    ((wControl_p)b)->outline = FALSE; +      if (option&BC_HORZ) {          b->widget = gtk_hbox_new(FALSE, 0);      } else { @@ -604,6 +636,8 @@ wChoice_p wToggleCreate(          gtk_widget_show(butt);          g_signal_connect(GTK_OBJECT(butt), "toggled",                           G_CALLBACK(pushChoice), b); +        g_signal_connect_after(GTK_OBJECT(b->widget), "expose-event", +            					G_CALLBACK(exposeButt), b);          wlibAddHelpString(butt, helpStr);      } diff --git a/app/wlib/gtklib/color.c b/app/wlib/gtklib/color.c index 64b96ef..e1689d2 100644 --- a/app/wlib/gtklib/color.c +++ b/app/wlib/gtklib/color.c @@ -100,7 +100,7 @@ static colorMap_t colorMap[] = {      { 208, 208, 208 },	/* Gray */      { 224, 224, 224 },	/* Gray */      { 240, 240, 240 },	/* Gray */ -    {   0,   0,   0 }	/* BlackPixel */ +    { 255, 255, 255 }	/* WhitePixel */  };  #define NUM_GRAYS (16) @@ -125,7 +125,7 @@ wDrawColor wDrawColorGray(      if (n <= 0) {          return wDrawColorBlack; -    } else if (n > NUM_GRAYS) { +    } else if (n >= NUM_GRAYS) {          return wDrawColorWhite;      } else {          n = (n*256)/NUM_GRAYS; @@ -209,7 +209,7 @@ wDrawColor wDrawFindColor(      long rgb0)  {      wDrawColor cc; -    int r0, g0, b0; +    int r0, g0, b0, r1, g1, b1;      int d0;      int i;      colorMap_t tempMapValue; @@ -231,7 +231,10 @@ wDrawColor wDrawFindColor(          colorMap_t * cm_p;          cm_p = &g_array_index(colorMap_garray, colorMap_t, i); -        d1 = abs(r0-cm_p->red) + abs(g0-cm_p->green) + abs(b0-cm_p->blue); +        r1 = (int)cm_p->red; +        g1 = (int)cm_p->green; +        b1 = (int)cm_p->blue; +        d1 = abs(r0-r1) + abs(g0-g1) + abs(b0-b1);          if (d1 == 0) {              return i; diff --git a/app/wlib/gtklib/control.c b/app/wlib/gtklib/control.c index c891924..07d9210 100644 --- a/app/wlib/gtklib/control.c +++ b/app/wlib/gtklib/control.c @@ -35,7 +35,7 @@  #include "gtkint.h" -#define  GTKCONTROLHILITEWIDTH (3) +#define  GTKCONTROLHILITEWIDTH (4)  /**   * Cause the control <b> to be displayed or hidden. @@ -252,6 +252,46 @@ void wControlSetFocus(  {  } +wBool_t wControlExpose ( +		 GtkWidget * widget, +		 GdkEventExpose * event, +		 wControl_p b +		) +{ +	GdkWindow * win = gtk_widget_get_window(b->widget); +	cairo_t * cr = NULL; +	if (win) { +		cr = gdk_cairo_create(win); +	} else return TRUE; + +#ifdef CURSOR_SURFACE +	if (b && b->cursor_surface.surface && b->cursor_surface.show) { +		cairo_set_source_surface(cr,b->cursor_surface.surface,event->area.x, event->area.y); +		cairo_set_operator(cr,CAIRO_OPERATOR_OVER); +		cairo_rectangle(cr,event->area.x, event->area.y, +				event->area.width, event->area.height); +		cairo_fill(cr); +	} +#endif + +	if (b->outline) { +		cairo_set_source_rgb(cr, 0.23, 0.37, 0.80); +		cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); +		cairo_set_line_width(cr, GTKCONTROLHILITEWIDTH); +		cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); +		cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); +		cairo_rectangle(cr,event->area.x+2, event->area.y+2, +						event->area.width-4, event->area.height-4); +		cairo_stroke(cr); +	} + + +	cairo_destroy(cr); + + +    return FALSE; +} +  /**   * Draw a rectangle around a control   * \param b IN the control @@ -279,17 +319,8 @@ void wControlHilite(          return;      } -    cr = gdk_cairo_create(gtk_widget_get_window(b->parent->gtkwin)); -    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); -    cairo_set_operator(cr, CAIRO_OPERATOR_XOR); -    cairo_set_line_width(cr, GTKCONTROLHILITEWIDTH); -    cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); -    cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); -    cairo_rectangle(cr, -                    b->realX - GTKCONTROLHILITEWIDTH, -                    b->realY - off, -                    b->w + GTKCONTROLHILITEWIDTH, -                    b->h + off + 1); -    cairo_stroke(cr); -    cairo_destroy(cr); +    b->outline = hilite; + +    if (b->widget) +    	gtk_widget_queue_draw(b->widget);  } diff --git a/app/wlib/gtklib/droplist.c b/app/wlib/gtklib/droplist.c index 5fbdd17..69a2efd 100644 --- a/app/wlib/gtklib/droplist.c +++ b/app/wlib/gtklib/droplist.c @@ -235,43 +235,16 @@ wBool_t wDropListSetValues(  }  /** - * Signal handler for the "changed"-signal in drop list's entry field. - * Get the entered text and calls the 'action' for handling of entered - * value. - * * - * \param entry IN entry field of the droplist - * \param data IN the drop list handle - * \return - */ - -static void DropListEntryEntered( -    GtkEntry * entry, -    gpointer userData) -{ -    const gchar * text; - -    text = gtk_entry_get_text(entry); - -    if (text && *text != '\0') { -        gchar *copyOfText = g_strdup(text); -        ((wList_p)userData)->editted = TRUE; -        ((wList_p)userData)->action(-1, copyOfText, 1, ((wList_p)userData)->data, NULL); -        g_free((gpointer)copyOfText); -    } else { -        wBeep(); -    } -} - -/** - * Signal handler for the "changed"-signal in drop list. Gets the selected - * text and determines the selected row in the tree model. + * Signal handler for the "changed"-signal in drop list. + * Gets the selected text and determines the selected row in the tree model. + * Or handles user entered text.   *   * \param comboBox IN the combo_box   * \param data IN the drop list handle   * \return   */ -static int DropListSelectChild( +static int DropListChanged(      GtkComboBox * comboBox,      gpointer data)  { @@ -279,17 +252,14 @@ static int DropListSelectChild(      GtkTreeIter iter;      wIndex_t inx = 0; -    gchar *string; -    wListItem_p addData; +    gchar *string = NULL; +    wListItem_p listItemP = NULL;      if (bl->recursion) {          return 0;      } -    bl->editted = FALSE; - -    /* Obtain currently selected item from combo box. -     * If nothing is selected, do nothing. */ +    /* Obtain currently selected item from combo box. */      if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(comboBox), &iter)) {          GtkTreeModel *model; @@ -301,19 +271,30 @@ static int DropListSelectChild(                   &iter);          inx = atoi(string);          g_free(string); +        string = NULL;          /* Obtain string from model. */          gtk_tree_model_get(model, &iter,                             LISTCOL_TEXT, &string, -                           LISTCOL_DATA, (void *)&addData, +                           LISTCOL_DATA, (void *)&listItemP,                             -1); +        bl->editted = FALSE;      } else { -        return 0; +	/* Nothing selected, user is entering text directly */ +        inx = -1; +        GtkEntry * entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(bl->widget))); +	if ( entry == NULL ) +            return 0; +        const char * string1 = gtk_entry_get_text(entry); +	if ( string1 == NULL ) +            return 0; +        string = g_strdup(string1); +        bl->editted = TRUE;      }      /* selection changed, store new selections and call back */ -    if (bl->last != inx) { +    if (bl->last != inx || bl->editted == TRUE) {          bl->last = inx; @@ -323,11 +304,12 @@ static int DropListSelectChild(          /* selection changed -> callback */          if (string && bl->action) { -            bl->action(inx, string, 1, bl->data, addData->itemData); +            bl->action(inx, string, 1, bl->data, listItemP?listItemP->itemData:NULL);          }      } -    g_free(string); +    if ( string ) +        g_free(string);      return 1;  } @@ -429,14 +411,7 @@ wList_p wDropListCreate(      gtk_widget_set_name(b->widget,"mycombo");      g_signal_connect(GTK_OBJECT(b->widget), "changed", -                     G_CALLBACK(DropListSelectChild), b); - -    if (option & BL_EDITABLE) { -        g_signal_connect(gtk_bin_get_child(GTK_BIN(b->widget)), -                         "changed", -                         G_CALLBACK(DropListEntryEntered), -                         b); -    } +                     G_CALLBACK(DropListChanged), b);      gtk_widget_set_size_request(b->widget, width, -1); diff --git a/app/wlib/gtklib/filesel.c b/app/wlib/gtklib/filesel.c index ca30c7f..a1fb7cc 100644 --- a/app/wlib/gtklib/filesel.c +++ b/app/wlib/gtklib/filesel.c @@ -34,24 +34,92 @@  #define GSEAL_ENABLE  #include <gtk/gtk.h> +#include <glib-object.h>  #include "gtkint.h"  #include "i18n.h"  #define MAX_ALLOWEDFILTERS 10  struct wFilSel_t { -		GtkWidget * window; -		wFilSelCallBack_p action; -		void * data; -		int pattCount; -		GtkFileFilter *filter[ MAX_ALLOWEDFILTERS ]; -		wFilSelMode_e mode; -		int opt; -		const char * title; -		wWin_p parent; -		char *defaultExtension; +		GtkWidget * window; 							/**<  file selector handle*/ +		wFilSelCallBack_p action; 						/**<  */ +		void * data; 									/**<  */ +		int pattCount; 									/**<  number of file patterns*/ +		wBool_t loadPatternsAdded;						/** Already loaded        	*/ +		GtkFileFilter *filter[ MAX_ALLOWEDFILTERS ]; 	/**< array of file patterns */ +		wFilSelMode_e mode; 							/**< used for load or save */ +		int opt; 										/**< see FS_ options */ +		const char * title; 							/**< dialog box title */ +		wWin_p parent; 									/**< parent window */ +		char *defaultExtension; 						/**< to use if no extension specified */  		}; +/** + * Signal handler for 'changed' signal of custom combo box. The filter + * is set accordinng to the file format active in the combo box + *  + * \param comboBox the combo box  + * \param fileSelector data of the file selector + *  + */ + +static void FileFormatChanged( GtkWidget *comboBox,  +						  struct wFilSel_t *fileSelector ) +{ +	// get active entry +	int entry = (int)gtk_combo_box_get_active (GTK_COMBO_BOX(comboBox)); +	 +	if( entry>=0 ) { +		g_object_ref(G_OBJECT( (fileSelector->filter)[ entry ]));  +		gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fileSelector->window ),						 +									(fileSelector->filter)[ entry ]); +	} +} + +/** + * Create a widget containing a combo box for selecting a file format.  + * From an array of filters, the names are retrieved and used to populate + * the combo box.  + * \param IN dialogBox + * \param patterns IN number of entries for combo + * \param filters IN + * \returns the newly created widget + */ +  +static GtkWidget *CreateFileformatSelector(struct wFilSel_t *dialogBox,  +			int patterns,  +			GtkFileFilter **filters) +{ +	GtkWidget *hbox = gtk_hbox_new(FALSE, 12); +	GtkWidget *text = gtk_label_new(_("Save format:")); +	GtkWidget *combo = gtk_combo_box_text_new (); + +	g_signal_connect(G_OBJECT(combo),  +				 "changed", +				 (GCallback)FileFormatChanged, +				 dialogBox ); + + +	gtk_box_pack_start (GTK_BOX(hbox), +				text, +				FALSE, +				FALSE, +				0); +	gtk_box_pack_end (GTK_BOX(hbox), +				combo, +				TRUE, +				TRUE, +				0); +	for(int i=0; i < patterns; i++ ) { +		const char *nameOfFilter = gtk_file_filter_get_name( filters[ i ] ); +		gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(combo), nameOfFilter ); +	} +	gtk_combo_box_set_active (GTK_COMBO_BOX(combo), 0); +	 +	gtk_widget_show_all(hbox); +	 +	return(hbox);             +}  /**   * Create a new file selector. Only the internal data structures are @@ -89,31 +157,75 @@ struct wFilSel_t * wFilSelCreate(  	fs->title = strdup( title );  	fs->action = action;  	fs->data = data; +	fs->pattCount = 0; +	fs->loadPatternsAdded = FALSE;  	if (pattList) { -		char * cp = strdup(pattList); +		char * cps = strdup(pattList); +		char *cp, *cp2;  		int count = 0; +		char *patternState, *patternState2;  		//create filters for the passed filter list  		// names and patterns are separated by | -		cp = strtok( cp, "|" );		 -		while ( cp  && count < (MAX_ALLOWEDFILTERS - 1)) { +		// filter elements are also separated by | +		cp = cps; +		while (cp && cp[0]) { +			if (cp[0] == '|') { +				count++; +				if (count && count%2==0) { +					cp[0] = ':';             //Replace every second "|" with ":" +				} +			} +			cp++; +		} +		count = 0; +		cp = cps;							//Restart +		if (opt&FS_PICTURES) {				//Put first  			fs->filter[ count ] = gtk_file_filter_new (); -			gtk_file_filter_set_name ( fs->filter[ count ], cp ); -			cp = strtok( NULL, "|" ); -			gtk_file_filter_add_pattern (fs->filter[ count ], cp ); -			// the first pattern is considered to match the default extension -			if( count == 0 ) { -				fs->defaultExtension = strdup( cp ); -			}	 -			cp = strtok( NULL, "|" ); -			count++; +			g_object_ref_sink( G_OBJECT(fs->filter[ count ] )); +			gtk_file_filter_set_name( fs->filter[ count ], _("Image files") ); +			gtk_file_filter_add_pixbuf_formats( fs->filter[ count ]); +			fs->pattCount = ++count; +		} +		cp = strtok_r( cp, ":", &patternState );          // Break up by colons +		while ( cp  && count < (MAX_ALLOWEDFILTERS - 1)) { +			cp2 = strtok_r( cp, "|", &patternState2 ); +			if (cp2) { +				fs->filter[ count ] = gtk_file_filter_new (); +				gtk_file_filter_set_name ( fs->filter[ count ], cp2 ); + +				cp2 = strtok_r( NULL, "|", &patternState2 ); +				// find multiple patterns separated by ";" +				if (cp2) { +					char * cp1s = strdup(cp2); +					char *cp1; +					char *filterState; + +					cp1 = cp1s; +					cp1 = strtok_r(cp1, ";", &filterState ); +					while (cp1) { +						gtk_file_filter_add_pattern (fs->filter[ count ], cp1 ); +						cp1 = strtok_r(NULL, ";", &filterState ); +					} +					if (cp1s) +						free(cp1s); +				} +				// the first pattern is considered to match the default extension +				if( count == 0 && !(opt&FS_PICTURES)) { +					fs->defaultExtension = strdup( cp2 ); +					int i = 0; +					for (i=0; i<strlen(cp2) && cp2[i] != ' ' && cp2[i] != ';';i++) ; +					if (i<strlen(cp2)) fs->defaultExtension[i] = '\0'; +				} +				fs->pattCount = ++count; +			} +			cp = strtok_r( NULL, ":", &patternState );  		} -		// finally add the all files pattern -		fs->filter[ count ] = gtk_file_filter_new (); -		gtk_file_filter_set_name( fs->filter[ count ], _("All files") ); -		gtk_file_filter_add_pattern( fs->filter[ count ], "*" ); -		fs->pattCount = count++; +		if (cps)  +			free(cps); + +  	} else {  		fs->filter[ 0 ] = NULL;  		fs->pattCount = 0; @@ -145,33 +257,41 @@ int wFilSelect( struct wFilSel_t * fs, const char * dirName )  										   (fs->mode == FS_LOAD ? GTK_STOCK_OPEN : GTK_STOCK_SAVE ), GTK_RESPONSE_ACCEPT,  										   NULL );  		if (fs->window==0) abort(); -		// get confirmation before overwritting an existing file									 -		gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(fs->window), TRUE ); +		if ( fs->mode == FS_SAVE ) { +			// get confirmation before overwritting an existing file									 +			gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(fs->window), TRUE ); +		} -		// allow selecting multiple files -		if( fs->opt & FS_MULTIPLEFILES ) { -			gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(fs->window), TRUE); -		}	 -		// add the file filters to the dialog box -		if( fs->pattCount ) { -			for( i = 0; i <= fs->pattCount; i++ ) { -				gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( fs->window ), fs->filter[ i ] );  -			} -		}												  		/** \todo for loading a shortcut folder could be added linking to the example directory */  	}  	strcpy( name, dirName ); -	if( fs->mode == FS_SAVE ) -		gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(fs->window), name );  +	gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(fs->window), name );  +	if( fs->mode == FS_SAVE || fs->mode == FS_UPDATE ) { +		gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER(fs->window),  +				CreateFileformatSelector(fs, fs->pattCount, fs->filter )); +	}	      // Add a current folder and a shortcut to it for Load/import dialogs      if( fs->mode == FS_LOAD ) { -        gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(fs->window), name );          gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER(fs->window), name, NULL ); +		// allow selecting multiple files +		if( fs->opt & FS_MULTIPLEFILES ) { +			gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(fs->window), TRUE); +		}	 +		// add the file filters to the dialog box +		if( fs->pattCount && !fs->loadPatternsAdded) { + +			for( i = 0; i < fs->pattCount; i++ ) { +				gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( fs->window ), fs->filter[ i ] );  +			} +			fs->loadPatternsAdded = TRUE; +		}												      } -	if( gtk_dialog_run( GTK_DIALOG( fs->window )) == GTK_RESPONSE_ACCEPT ) { +    int resp = gtk_dialog_run( GTK_DIALOG( fs->window )); + +	if( resp == GTK_RESPONSE_ACCEPT || resp == GTK_RESPONSE_APPLY) {  		char **fileNames;	  		GSList *fileNameList; @@ -184,21 +304,36 @@ int wFilSelect( struct wFilSel_t * fs, const char * dirName )  			file = g_filename_from_uri( g_slist_nth_data( fileNameList, i ), &host, &err );  			// check for presence of file extension -			// jump behind tha last directory delimiter +			// jump behind the last directory delimiter  			namePart = strrchr( file, '/' ) + 1;  			// is there a dot in the last part, yes->extension present  			if( !strchr( namePart, '.' ) ){ -				// make room for the extension -				file = g_realloc( file, strlen(file)+strlen(fs->defaultExtension)); -				strcat( file, fs->defaultExtension + 1 ); +				 +				// else try to find the current filter and parse its name +				GtkFileFilter *currentFilter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER(fs->window) ); +				if (currentFilter) { +					const char *nameOfFilter = gtk_file_filter_get_name( currentFilter ); +					char *pattern = strdup( nameOfFilter ); +					char *extension = fs->defaultExtension; +					char *startDelimiter = strstr( pattern, "(*." ); + +					if(startDelimiter) { +						char *endDelimiter = strpbrk(startDelimiter + 3, ",;) "); +						if( endDelimiter ) { +							*endDelimiter = '\0'; +							extension = startDelimiter + 2; +						} +					} +					file = g_realloc( file, strlen(file)+strlen(extension)); +					strcat( file, extension ); +					free( pattern ); +				}  			}	  			fileNames[ i ] = file;  			g_free( g_slist_nth_data ( fileNameList, i));  		} -		if (fs->data) -			strcpy( fs->data, fileNames[ 0 ] ); -		 +		gtk_widget_hide( GTK_WIDGET( fs->window ));  		if (fs->action) {  			fs->action( g_slist_length(fileNameList), fileNames, fs->data );  		} @@ -208,8 +343,9 @@ int wFilSelect( struct wFilSel_t * fs, const char * dirName )  		}  		free( fileNames );  		g_slist_free (fileNameList);	 +	} else { +		gtk_widget_hide( GTK_WIDGET( fs->window ));  	} -	gtk_widget_hide( GTK_WIDGET( fs->window ));  	return 1;  } diff --git a/app/wlib/gtklib/font.c b/app/wlib/gtklib/font.c index 68ba87b..e2f741b 100644 --- a/app/wlib/gtklib/font.c +++ b/app/wlib/gtklib/font.c @@ -185,7 +185,8 @@ PangoLayout *wlibFontCreatePangoLayout(GtkWidget *widget,                                         int *width_p,                                         int *height_p,                                         int *ascent_p, -                                       int *descent_p) +                                       int *descent_p, +									   int *baseline_p)  {      if (!fontInitted) {          fontInit(); @@ -214,14 +215,16 @@ PangoLayout *wlibFontCreatePangoLayout(GtkWidget *widget,                                      FONTSIZE_TO_PANGOSIZE(fs) * PANGO_SCALE);      pango_layout_set_font_description(layout, fontDescription);      /* get layout measures */ -    pango_layout_get_pixel_size(layout, width_p, height_p); +    pango_layout_get_size(layout, width_p, height_p); +    *width_p = *width_p / PANGO_SCALE; +    *height_p = *height_p / PANGO_SCALE;      context = gtk_widget_create_pango_context(widget);      metrics = pango_context_get_metrics(context, fontDescription,                                          pango_context_get_language(context)); -    *ascent_p  = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); -    *descent_p = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); +    *baseline_p = pango_layout_get_baseline(layout) / PANGO_SCALE; +    *ascent_p  = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; +    *descent_p = pango_font_metrics_get_descent(metrics) / PANGO_SCALE;      pango_font_metrics_unref(metrics); -    g_object_ref_sink(context);      g_object_unref(context);  #if WLIB_FONT_DEBUG >= 3      fprintf(stderr, "font layout created:\n"); @@ -347,9 +350,9 @@ wFontSize_t wSelectedFontSize(void)   * \return    describe the return value   */ -void wSetSelectedFontSize(int size) +void wSetSelectedFontSize(wFontSize_t size)  { -    absoluteFontSize = (wFontSize_t)size; +    absoluteFontSize = size;  }  /** diff --git a/app/wlib/gtklib/gtkdraw-cairo.c b/app/wlib/gtklib/gtkdraw-cairo.c index 5042667..4498a2c 100644 --- a/app/wlib/gtklib/gtkdraw-cairo.c +++ b/app/wlib/gtklib/gtkdraw-cairo.c @@ -32,6 +32,9 @@  #include <gtk/gtk.h>  #include <gdk/gdk.h> +// Trace low level drawing actions +int iDrawLog = 0; +  #include "gtkint.h"  #include "gdk/gdkkeysyms.h" @@ -41,12 +44,16 @@  static long drawVerbose = 0; +// Hack to do TempRedraw or MainRedraw +// For Windows only +wBool_t wDrawDoTempDraw = TRUE; +  struct wDrawBitMap_t {  		int w;  		int h;  		int x;  		int y; -		const char * bits; +		const unsigned char * bits;  		GdkPixmap * pixmap;  		GdkBitmap * mask;  		}; @@ -102,64 +109,148 @@ struct wDraw_t psPrint_d;   *  *******************************************************************************/ - - -static GdkGC * selectGC( -		wDraw_p bd, +static cairo_t* gtkDrawCreateCairoCursorContext( +		wControl_p ct, +		cairo_surface_t * surf,  		wDrawWidth width,  		wDrawLineType_e lineType,  		wDrawColor color,  		wDrawOpts opts )  { -	if(width < 0.0) -	{ -		width = - width; -	} +	cairo_t* cairo; + +	cairo = cairo_create(surf); + +	width = width ? abs(width) : 1; +	cairo_set_line_width(cairo, width); + +	cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT); +	cairo_set_line_join(cairo, CAIRO_LINE_JOIN_MITER); -	if(opts & wDrawOptTemp) +	switch(lineType)  	{ -		if(bd->lastColor != color || !bd->lastColorInverted) +		case wDrawLineSolid:  		{ -			gdk_gc_set_foreground( bd->gc, wlibGetColor(color,FALSE) ); -			bd->lastColor = color; -			bd->lastColorInverted = TRUE; +			cairo_set_dash(cairo, 0, 0, 0); +			break;  		} -		gdk_gc_set_function( bd->gc, GDK_XOR ); -		gdk_gc_set_line_attributes( bd->gc, width, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER ); -	} -	else -	{ -		if(bd->lastColor != color || bd->lastColorInverted) +		case wDrawLineDash:  		{ -			gdk_gc_set_foreground( bd->gc, wlibGetColor(color,TRUE) ); -			bd->lastColor = color; -			bd->lastColorInverted = FALSE; +			double dashes[] = { 5, 3 }; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0); +			break;  		} -		gdk_gc_set_function( bd->gc, GDK_COPY ); -		if (lineType==wDrawLineDash) +		case wDrawLineDot:  		{ -			gdk_gc_set_line_attributes( bd->gc, width, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER ); +			double dashes[] = { 1, 2 }; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0); +			break;  		} -		else +		case wDrawLineDashDot: +		{ +			double dashes[] = { 5, 2, 1, 2 }; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0); +			break; +		} +		case wDrawLineDashDotDot:  		{ -			gdk_gc_set_line_attributes( bd->gc, width, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER ); +			double dashes[] = { 5, 2, 1, 2, 1, 2 }; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0); +			break; +		} +		case wDrawLineCenter: +		{ +			double dashes[] = { 8, 3, 5, 3}; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0.0); +			break; +		} +		case wDrawLinePhantom: +		{ +			double dashes[] = { 8, 3, 5, 3, 5, 3}; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0.0); +			break;  		} -		gdk_gc_set_function(bd->gc, GDK_NOOP);  	} -	return bd->gc; +	GdkColor * gcolor; + + +	cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); +	gcolor = wlibGetColor(color, TRUE); + +	if (ct->type == B_DRAW)  { +		wDraw_p bd = (wDraw_p)ct; +		bd->lastColor = color; +	} + +	cairo_set_source_rgba(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0, 1.0); + +	return cairo; +} + + +wBool_t wDrawSetTempMode( +	wDraw_p bd, +	wBool_t bTemp ) +{ +	wBool_t ret = bd->bTempMode; +	bd->bTempMode = bTemp; +	if ( ret == FALSE && bTemp == TRUE ) { +		// Main to Temp drawing +		wDrawClearTemp( bd ); +	} +	return ret;  }  static cairo_t* gtkDrawCreateCairoContext(  		wDraw_p bd, +		GdkDrawable * win,  		wDrawWidth width,  		wDrawLineType_e lineType,  		wDrawColor color,  		wDrawOpts opts )  { -	cairo_t* cairo = gdk_cairo_create(bd->pixmap); +	cairo_t* cairo; + +	if (win) +		cairo = gdk_cairo_create(win); +	else { +		if (opts & wDrawOptTemp) { +			if ( ! bd->bTempMode ) +				printf( "Temp draw in Main Mode. Contact Developers. See %s:%d\n", "gtkdraw-cario.c", __LINE__+1 ); +/* Temp Draw In Main Mode: +	You are seeing this message because there is a wDraw*() call on tempD but you are not in the context of TempRedraw() +	Typically this happens when Cmd<Object>() is processing a C_DOWN or C_MOVE action and it writes directly to tempD +	Instead it sould set some state which allows c_redraw to do the actual drawing +	If you set a break point on the printf you'll see the offending wDraw*() call in the traceback +	It should be sufficient to remove that draw code or move it to C_REDRAW +	This is not fatal but the draw will be ineffective because the next TempRedraw() will erase the temp surface +	before the expose event can copy (or bitblt) it +*/ +			cairo = cairo_create(bd->temp_surface); +		} else { +			if ( bd->bTempMode ) +				printf( "Main draw in Temp Mode. Contact Developers. See %s:%d\n", "gtkdraw-cario.c", __LINE__+1 ); +/* Main Draw In Temp Mode: +	You are seeing this message because there is a wDraw*() call on mainD but you are in the context of TempRedraw() +	Typically this happens when C_REDRAW action calls wDraw*() on mainD, in which case it should be writing to tempD. +	Or the wDraw*() call should be removed if it is redundant. +	If you set a break point on the printf you'll see the offending wDraw*() call in the traceback +	This is not fatal but could result in garbage being left on the screen if the command is cancelled. +*/ +			cairo = gdk_cairo_create(bd->pixmap); +		} +	}  	width = width ? abs(width) : 1; +	if ( color == wDrawColorWhite ) +		width += 1;  // Remove ghosts  	cairo_set_line_width(cairo, width);  	cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT); @@ -179,24 +270,52 @@ static cairo_t* gtkDrawCreateCairoContext(  			cairo_set_dash(cairo, dashes, len_dashes, 0);  			break;  		} -	} +		case wDrawLineDot: +		{ +			double dashes[] = { 1, 2 }; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0); +			break; +		} +		case wDrawLineDashDot: +		{ +			double dashes[] = { 5, 2, 1, 2 }; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0); +			break; +		} +		case wDrawLineDashDotDot: +		{ +			double dashes[] = { 5, 2, 1, 2, 1, 2 }; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0); +			break; +		} +		case wDrawLineCenter: +		{ +			double dashes[] = { 8, 3, 5, 3}; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0.0); +			break; +		} +		case wDrawLinePhantom: +		{ +			double dashes[] = { 8, 3, 5, 3, 5, 3}; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cairo, dashes, len_dashes, 0.0); +			break; +		} -	if(opts & wDrawOptTemp) -	{ -		cairo_set_source_rgba(cairo, 0, 0, 0, 0); -		cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); -	} -	else -	{ -		long rgbcolor = wDrawGetRGB(color); -		int r0, g0, b0; -		r0 = (int)(rgbcolor>>16)&0xFF; -		g0 = (int)(rgbcolor>>8)&0xFF; -		b0 = (int)(rgbcolor)&0xFF; -		cairo_set_source_rgb(cairo, r0/255.0, g0/255.0, b0/255.0); - -		cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);  	} +	GdkColor * gcolor; + + +	cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); +	gcolor = wlibGetColor(color, TRUE); + +	bd->lastColor = color; + +	cairo_set_source_rgb(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0);  	return cairo;  } @@ -206,6 +325,36 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  	return NULL;  } +#ifdef CURSOR_SURFACE +cairo_t* CreateCursorSurface(wControl_p ct, wSurface_p surface, wPos_t width, wPos_t height, wDrawColor color, wDrawOpts opts) { + +		cairo_t * cairo = NULL; + +		if ((opts&wDrawOptCursor) || (opts&wDrawOptCursorRmv)) { + +			if (surface!=NULL || surface->width != width || surface->height != height) { +				if (surface->surface) cairo_surface_destroy(surface->surface); +				surface->surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width,height ); +				surface->width = width; +				surface->height = height; + +			} + +			cairo = gtkDrawCreateCairoCursorContext(ct,surface->surface,0,wDrawLineSolid, color, opts); +			cairo_save(cairo); +			cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 0.0); +			cairo_paint(cairo); +			cairo_restore(cairo); +			surface->show = TRUE; +			cairo_set_operator(cairo,CAIRO_OPERATOR_SOURCE); + +		} + +		return cairo; + +} +#endif +    void wDrawDelayUpdate(  		wDraw_p bd,  		wBool_t delay ) @@ -239,36 +388,19 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		psPrintLine( x0, y0, x1, y1, width, lineType, color, opts );  		return;  	} -	gc = selectGC( bd, width, lineType, color, opts );  	x0 = INMAPX(bd,x0);  	y0 = INMAPY(bd,y0);  	x1 = INMAPX(bd,x1);  	y1 = INMAPY(bd,y1); -	gdk_draw_line( bd->pixmap, gc, x0, y0, x1, y1 ); -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, width, lineType, color, opts); +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, width, lineType, color, opts);  	cairo_move_to(cairo, x0 + 0.5, y0 + 0.5);  	cairo_line_to(cairo, x1 + 0.5, y1 + 0.5);  	cairo_stroke(cairo);  	gtkDrawDestroyCairoContext(cairo); +	if (bd->widget) +		gtk_widget_queue_draw(GTK_WIDGET(bd->widget)); //,x0,y0+1,x1,y1+1); -	if ( bd->delayUpdate || bd->widget == NULL ) return; -	width /= 2; -	if (x0 < x1) { -		update_rect.x = x0-1-width; -		update_rect.width = x1-x0+2+width+width; -	} else { -		update_rect.x = x1-1-width; -		update_rect.width = x0-x1+2+width+width; -	} -	if (y0 < y1) { -		update_rect.y = y0-1-width; -		update_rect.height = y1-y0+2+width+width; -	} else { -		update_rect.y = y1-1-width; -		update_rect.height = y0-y1+2+width+width; -	} -	gtk_widget_draw( bd->widget, &update_rect );  }  /** @@ -299,33 +431,20 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		wDrawOpts opts )  {  	int x, y, w, h; -	GdkGC * gc; -	GdkRectangle update_rect;  	if ( bd == &psPrint_d ) {  		psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts );  		return;  	} -	gc = selectGC( bd, width, lineType, color, opts ); +  	if (r < 6.0/75.0) return;  	x = INMAPX(bd,x0-r);  	y = INMAPY(bd,y0+r);  	w = 2*r;  	h = 2*r; -	// remove the old arc -	gdk_draw_arc( bd->pixmap, gc, FALSE, x, y, w, h, (int)((-angle0 + 90)*64.0), (int)(-angle1*64.0) ); - -	// and its center point -	if (drawCenter) { -		x = INMAPX(bd,x0); -		y = INMAPY(bd,y0); -		gdk_draw_line( bd->pixmap, gc, x - ( CENTERMARK_LENGTH/2), y, x + ( CENTERMARK_LENGTH/2), y ); -		gdk_draw_line( bd->pixmap, gc, x, y - ( CENTERMARK_LENGTH/2), x, y + ( CENTERMARK_LENGTH/2)); -	} -  	// now create the new arc -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, width, lineType, color, opts); +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, width, lineType, color, opts);  	cairo_new_path(cairo);  	// its center point marker @@ -337,6 +456,7 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		cairo_move_to(cairo, INMAPX(bd, x0), INMAPY(bd, y0 - (CENTERMARK_LENGTH / 2 )));  		cairo_line_to(cairo, INMAPX(bd, x0) , INMAPY(bd, y0  + (CENTERMARK_LENGTH / 2)));  		cairo_new_sub_path( cairo ); +  	}  	// draw the curve itself @@ -344,14 +464,10 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  	cairo_stroke(cairo);  	gtkDrawDestroyCairoContext(cairo); +	if (bd->widget && !bd->delayUpdate) +			gtk_widget_queue_draw_area(bd->widget,x,y,w,h); + -	if ( bd->delayUpdate || bd->widget == NULL) return; -	width /= 2; -	update_rect.x = x-1-width; -	update_rect.y = y-1-width; -	update_rect.width = w+2+width+width; -	update_rect.height = h+2+width+width; -	gtk_widget_draw( bd->widget, &update_rect );  } @@ -361,28 +477,21 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		wDrawColor color,  		wDrawOpts opts )  { -	GdkGC * gc;  	GdkRectangle update_rect;  	if ( bd == &psPrint_d ) {  		/*psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts );*/  		return;  	} -	gc = selectGC( bd, 0, wDrawLineSolid, color, opts ); -	gdk_draw_point( bd->pixmap, gc, INMAPX(bd, x0 ), INMAPY(bd, y0 ) ); -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opts); +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts);  	cairo_new_path(cairo);  	cairo_arc(cairo, INMAPX(bd, x0), INMAPY(bd, y0), 0.75, 0, 2 * M_PI);  	cairo_stroke(cairo);  	gtkDrawDestroyCairoContext(cairo); +	if (bd->widget && !bd->delayUpdate) +		gtk_widget_queue_draw_area(bd->widget,INMAPX(bd,x0-0.75),INMAPY(bd,y0+0.75),2,2); -	if ( bd->delayUpdate || bd->widget == NULL) return; -	update_rect.x = INMAPX(bd, x0 )-1; -	update_rect.y = INMAPY(bd, y0 )-1; -	update_rect.width = 2; -	update_rect.height = 2; -	gtk_widget_draw( bd->widget, &update_rect );  }  /******************************************************************************* @@ -407,6 +516,7 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  	int h;  	gint ascent;  	gint descent; +	gint baseline;  	double angle = -M_PI * a / 180.0;  	if ( bd == &psPrint_d ) { @@ -418,23 +528,27 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  	y = INMAPY(bd,y);  	/* draw text */ -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opts); +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts);  	cairo_save( cairo ); -	cairo_translate( cairo, x, y ); -	cairo_rotate( cairo, angle ); +	cairo_identity_matrix(cairo);  	layout = wlibFontCreatePangoLayout(bd->widget, cairo, fp, fs, s,  									  (int *) &w, (int *) &h, -									  (int *) &ascent, (int *) &descent); +									  (int *) &ascent, (int *) &descent, (int *) &baseline); -	/* cairo does not support the old method of text removal by overwrite; force always write here and -           refresh on cancel event */ +	/* cairo does not support the old method of text removal by overwrite; +	 * if color is White, then overwrite old text with a White rectangle */  	GdkColor* const gcolor = wlibGetColor(color, TRUE);  	cairo_set_source_rgb(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0); -	cairo_move_to( cairo, 0, -ascent ); +	cairo_translate( cairo, x, y ); +	cairo_rotate( cairo, angle ); +	cairo_translate( cairo, 0, -baseline); + +	cairo_move_to(cairo, 0, 0); +	pango_cairo_update_layout(cairo, layout);  	pango_cairo_show_layout(cairo, layout);  	wlibFontDestroyPangoLayout(layout);  	cairo_restore( cairo ); @@ -446,17 +560,21 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  	 * for simplicity sake I added plain text height ascent and descent,  	 * mathematically correct would be to use the trigonometrical functions as well  	 */ -	update_rect.x      = (gint) x - ascent - descent - 1; -	update_rect.y      = (gint) y - (gint) ascent - 1; -	update_rect.width  = (gint) (w * cos( angle ) + 2 + ascent + descent); -	update_rect.height = (gint) (h * sin( angle ) + ascent + descent + 2 ); +	update_rect.x      = (gint) x - 2; +	update_rect.y      = (gint) y - (gint) (baseline + descent) - 2; +	update_rect.width  = (gint) (w * cos( angle ) + h * sin(angle))+2; +	update_rect.height = (gint) (h * sin( angle ) + w * cos(angle))+2;  	gtk_widget_draw(bd->widget, &update_rect); +	if (bd->widget && !bd->delayUpdate) +		gtk_widget_queue_draw_area(bd->widget, update_rect.x, update_rect.y, update_rect.width, update_rect.height); +      }   void wDrawGetTextSize(  		wPos_t *w,  		wPos_t *h,  		wPos_t *d, +		wPos_t *a,  		wDraw_p bd,  		const char * s,  		wFont_p fp, @@ -466,21 +584,31 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  	int textHeight;  	int ascent;  	int descent; +	int baseline;  	*w = 0;  	*h = 0; +	/* draw text */ +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, wDrawColorBlack, bd->bTempMode?wDrawOptTemp:0 ); + +	cairo_identity_matrix(cairo); +  	wlibFontDestroyPangoLayout( -		wlibFontCreatePangoLayout(bd->widget, NULL, fp, fs, s, +		wlibFontCreatePangoLayout(bd->widget, cairo, fp, fs, s,  								 &textWidth, (int *) &textHeight, -								 (int *) &ascent, (int *) &descent)); +								 (int *) &ascent, (int *) &descent, (int *) &baseline) );  	*w = (wPos_t) textWidth;  	*h = (wPos_t) textHeight; -	*d = (wPos_t) textHeight-ascent; +	*a = (wPos_t) ascent; +	//*d = (wPos_t) textHeight-ascent; +	*d = (wPos_t) descent;  	if (debugWindow >= 3)  		fprintf(stderr, "text metrics: w=%d, h=%d, d=%d\n", *w, *h, *d); + +	gtkDrawDestroyCairoContext(cairo);  } @@ -490,6 +618,28 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {   *  *******************************************************************************/ +static void wlibDrawFilled( +	cairo_t * cairo, +	wDrawColor color, +	wDrawOpts opt ) +{ +	if ( (opt & wDrawOptTransparent) != 0 ) { +		if ( (opt & wDrawOptTemp) == 0 ) { +			cairo_set_source_rgb(cairo, 0,0,0); +			cairo_set_operator(cairo, CAIRO_OPERATOR_DIFFERENCE); +			cairo_fill_preserve(cairo); +		} +		GdkColor * gcolor = wlibGetColor(color, TRUE); +		cairo_set_source_rgba(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0, 1.0); +		cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); +		cairo_stroke_preserve(cairo); +		cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); +		cairo_set_source_rgba(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0, 0.3); +	} +	cairo_fill(cairo); +} + +   void wDrawFilledRectangle(  		wDraw_p bd,  		wPos_t x, @@ -499,7 +649,6 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		wDrawColor color,  		wDrawOpts opt )  { -	GdkGC * gc;  	GdkRectangle update_rect;  	if ( bd == &psPrint_d ) { @@ -507,47 +656,46 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		return;  	} -	gc = selectGC( bd, 0, wDrawLineSolid, color, opt );  	x = INMAPX(bd,x);  	y = INMAPY(bd,y)-h; -	gdk_draw_rectangle( bd->pixmap, gc, TRUE, x, y, w, h ); -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opt); +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opt);  	cairo_move_to(cairo, x, y);  	cairo_rel_line_to(cairo, w, 0);  	cairo_rel_line_to(cairo, 0, h);  	cairo_rel_line_to(cairo, -w, 0); -	cairo_fill(cairo); +	cairo_rel_line_to(cairo, 0, -h); +	wlibDrawFilled( cairo, color, opt ); +  	gtkDrawDestroyCairoContext(cairo); +	if (bd->widget && !bd->delayUpdate) +		gtk_widget_queue_draw_area(GTK_WIDGET(bd->widget),x,y,w,h); -	if ( bd->delayUpdate || bd->widget == NULL) return; -	update_rect.x = x-1; -	update_rect.y = y-1; -	update_rect.width = w+2; -	update_rect.height = h+2; -	gtk_widget_draw( bd->widget, &update_rect );  } - void wDrawFilledPolygon( + void wDrawPolygon(  		wDraw_p bd,  		wPos_t p[][2], +		wPolyLine_e type[],  		int cnt,  		wDrawColor color, -		wDrawOpts opt ) +		wDrawWidth dw, +		wDrawLineType_e lt, +		wDrawOpts opt, +		int fill, +		int open )  { -	GdkGC * gc;  	static int maxCnt = 0;  	static GdkPoint *points;  	int i; -	GdkRectangle update_rect;  	if ( bd == &psPrint_d ) { -		psPrintFillPolygon( p, cnt, color, opt ); +		psPrintFillPolygon( p, type, cnt, color, opt, fill, open );  		return;  	} -	if (cnt > maxCnt) { +		if (cnt > maxCnt) {  		if (points == NULL)  			points = (GdkPoint*)malloc( cnt*sizeof *points );  		else @@ -556,43 +704,100 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  			abort();  		maxCnt = cnt;  	} - -	update_rect.x = bd->w; -	update_rect.y = bd->h; -	update_rect.width = 0; -	update_rect.height = 0; -	for (i=0; i<cnt; i++) { -		points[i].x = INMAPX(bd,p[i][0]); -		points[i].y = INMAPY(bd,p[i][1]); -		if (update_rect.x > points[i].x) -			update_rect.x = points[i].x; -		if (update_rect.width < points[i].x) -			update_rect.width = points[i].x; -		if (update_rect.y > points[i].y) -			update_rect.y = points[i].y; -		if (update_rect.height < points[i].y) -			update_rect.height = points[i].y; +	wPos_t min_x,max_x,min_y,max_y; +	min_x = max_x = INMAPX(bd,p[0][0]); +	min_y = max_y = INMAPY(bd,p[0][1]); +    for (i=0; i<cnt; i++) { +    	points[i].x = INMAPX(bd,p[i][0]); +    	if (points[i].x < min_x) min_x = points[i].x; +    	if (points[i].x > max_x) max_x = points[i].x; +    	if (points[i].y > max_y) max_y = points[i].y; +    	points[i].y = INMAPY(bd,p[i][1]);  	} -	update_rect.x -= 1; -	update_rect.y -= 1; -	update_rect.width -= update_rect.x-2; -	update_rect.height -= update_rect.y-2; -	gc = selectGC( bd, 0, wDrawLineSolid, color, opt ); -	gdk_draw_polygon( bd->pixmap, gc, TRUE,	points, cnt ); - -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opt); + +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, fill?0:dw, fill?wDrawLineSolid:lt, color, opt); +  	for(i = 0; i < cnt; ++i)  	{ -		if(i) +		int j = i-1; +		int k = i+1; +		if (j < 0) j = cnt-1; +		if (k > cnt-1) k = 0; +		GdkPoint mid0, mid1, mid3, mid4; +		// save is static because of an apparent compiler bug on Linux +		// This happens with RelWithDebInfo target +		// If the first segment is a line then save should = points[0] +		// However it becomes mid0 instead which causes the last corner to be misplaced. +		static GdkPoint save; +		double len0, len1; +		double d0x = (points[i].x-points[j].x); +		double d0y = (points[i].y-points[j].y); +		double d1x = (points[k].x-points[i].x); +		double d1y = (points[k].y-points[i].y); +		len0 = (d0x*d0x+d0y*d0y); +		len1 = (d1x*d1x+d1y*d1y); +		mid0.x = (d0x/2)+points[j].x; +		mid0.y = (d0y/2)+points[j].y; +		mid1.x = (d1x/2)+points[i].x; +		mid1.y = (d1y/2)+points[i].y; +		if (type && (type[i] == wPolyLineRound) && (len1>0) && (len0>0)) { +			double ratio = sqrt(len0/len1); +			if (len0 < len1) { +				mid1.x = ((d1x*ratio)/2)+points[i].x; +				mid1.y = ((d1y*ratio)/2)+points[i].y; +			} else { +				mid0.x = points[i].x-(d0x/(2*ratio)); +				mid0.y = points[i].y-(d0y/(2*ratio)); +			} +		} +		mid3.x = (points[i].x-mid0.x)/2+mid0.x; +		mid3.y = (points[i].y-mid0.y)/2+mid0.y; +		mid4.x = (mid1.x-points[i].x)/2+points[i].x; +		mid4.y = (mid1.y-points[i].y)/2+points[i].y; +		points[i].x = round(points[i].x)+0.5; +		points[i].y = round(points[i].y)+0.5; +		mid0.x = round(mid0.x)+0.5; +		mid0.y = round(mid0.y)+0.5; +		mid1.x = round(mid1.x)+0.5; +		mid1.y = round(mid1.y)+0.5; +		mid3.x = round(mid3.x)+0.5; +		mid3.y = round(mid3.y)+0.5; +		mid4.x = round(mid4.x)+0.5; +		mid4.y = round(mid4.y)+0.5; +		if(i==0) { +			if (!type || type[i] == wPolyLineStraight || open) { +				cairo_move_to(cairo, points[i].x, points[i].y); +				save = points[0]; +			} else { +				cairo_move_to(cairo, mid0.x, mid0.y); +				if (type[i] == 1) +					cairo_curve_to(cairo, points[i].x, points[i].y, points[i].x, points[i].y, mid1.x, mid1.y); +				else +					cairo_curve_to(cairo, mid3.x, mid3.y, mid4.x, mid4.y, mid1.x, mid1.y); +				save = mid0; +			} +		} else if (!type || type[i] == wPolyLineStraight || (open && (i==cnt-1))) {  			cairo_line_to(cairo, points[i].x, points[i].y); -		else -			cairo_move_to(cairo, points[i].x, points[i].y); +		} else { +			cairo_line_to(cairo, mid0.x, mid0.y); +			if (type[i] == wPolyLineSmooth) +				cairo_curve_to(cairo, points[i].x, points[i].y, points[i].x, points[i].y, mid1.x, mid1.y); +			else +				cairo_curve_to(cairo, mid3.x, mid3.y, mid4.x, mid4.y, mid1.x, mid1.y); +		} +		if ((i==cnt-1) && !open) { +			cairo_line_to(cairo, save.x, save.y); +		} +	} +	if (fill && !open) { +		wlibDrawFilled( cairo, color, opt ); +	} else { +		cairo_stroke(cairo);  	} -	cairo_fill(cairo);  	gtkDrawDestroyCairoContext(cairo); +	if (bd->widget && !bd->delayUpdate) +			gtk_widget_queue_draw_area(GTK_WIDGET(bd->widget),min_x,min_y,max_x-min_y,max_y-min_y); -	if ( bd->delayUpdate || bd->widget == NULL) return; -	gtk_widget_draw( bd->widget, &update_rect );  }   void wDrawFilledCircle( @@ -603,60 +808,64 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		wDrawColor color,  		wDrawOpts opt )  { -	GdkGC * gc;  	int x, y, w, h; -	GdkRectangle update_rect;  	if ( bd == &psPrint_d ) {  		psPrintFillCircle( x0, y0, r, color, opt );  		return;  	} -	gc = selectGC( bd, 0, wDrawLineSolid, color, opt );  	x = INMAPX(bd,x0-r);  	y = INMAPY(bd,y0+r);  	w = 2*r;  	h = 2*r; -	gdk_draw_arc( bd->pixmap, gc, TRUE, x, y, w, h, 0, 360*64 ); -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opt); +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opt);  	cairo_arc(cairo, INMAPX(bd, x0), INMAPY(bd, y0), r, 0, 2 * M_PI); -	cairo_fill(cairo); +	wlibDrawFilled( cairo, color, opt );  	gtkDrawDestroyCairoContext(cairo); -	if ( bd->delayUpdate || bd->widget == NULL) return; -	update_rect.x = x-1; -	update_rect.y = y-1; -	update_rect.width = w+2; -	update_rect.height = h+2; -	gtk_widget_draw( bd->widget, &update_rect ); +	if (bd->widget) +			gtk_widget_queue_draw_area(GTK_WIDGET(bd->widget),x,y,w,h);  } + void wDrawClearTemp(wDraw_p bd) { +	//Wipe out temp space with 0 alpha (transparent) + +	static long cDCT = 0; +	if ( iDrawLog ) +		printf( "wDrawClearTemp %ld\n", cDCT++ ); +	cairo_t* cairo = cairo_create(bd->temp_surface); + +	cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 0.0); +	cairo_set_operator (cairo, CAIRO_OPERATOR_SOURCE); +	cairo_move_to(cairo, 0, 0); +	cairo_rel_line_to(cairo, bd->w, 0); +	cairo_rel_line_to(cairo, 0, bd->h); +	cairo_rel_line_to(cairo, -bd->w, 0); +	cairo_fill(cairo); +	cairo_destroy(cairo); + +	if (bd->widget && !bd->delayUpdate) +		gtk_widget_queue_draw(bd->widget); + }   void wDrawClear(  		wDraw_p bd )  { -	GdkGC * gc; -	GdkRectangle update_rect; -	gc = selectGC( bd, 0, wDrawLineSolid, wDrawColorWhite, 0 ); -	gdk_draw_rectangle(bd->pixmap, gc, TRUE, 0, 0, bd->w, bd->h); - -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, wDrawColorWhite, 0); +	cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, wDrawColorWhite, 0);  	cairo_move_to(cairo, 0, 0);  	cairo_rel_line_to(cairo, bd->w, 0);  	cairo_rel_line_to(cairo, 0, bd->h);  	cairo_rel_line_to(cairo, -bd->w, 0);  	cairo_fill(cairo); +	if (bd->widget) +		gtk_widget_queue_draw(bd->widget);  	gtkDrawDestroyCairoContext(cairo); -	if ( bd->delayUpdate || bd->widget == NULL) return; -	update_rect.x = 0; -	update_rect.y = 0; -	update_rect.width = bd->w; -	update_rect.height = bd->h; -	gtk_widget_draw( bd->widget, &update_rect ); +	wDrawClearTemp(bd);  }   void * wDrawGetContext( @@ -678,7 +887,7 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		int h,  		int x,  		int y, -		const char * fbits ) +		const unsigned char * fbits )  {  	wDrawBitMap_p bm; @@ -700,72 +909,107 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		wDrawColor color,  		wDrawOpts opts )  { -	//GdkGC * gc; -	GdkRectangle update_rect;  	int i, j, wb;  	wPos_t xx, yy;  	wControl_p b; -	GdkDrawable * gdk_window; +	wWin_p win; +	GdkDrawable * gdk_drawable, * cairo_surface; +	GtkWidget * widget = bd->widget; +	 +	static long cDBM = 0; +	if ( iDrawLog ) +		printf( "wDrawBitMap %ld\n", cDBM++ );  	x = INMAPX( bd, x-bm->x );  	y = INMAPY( bd, y-bm->y )-bm->h;  	wb = (bm->w+7)/8; -	//gc = selectGC( bd, 0, wDrawLineSolid, color, opts ); -	cairo_t* cairo = gtkDrawCreateCairoContext(bd, 0, wDrawLineSolid, color, opts); + +	cairo_t* cairo; + +#ifdef CURSOR_SURFACE +	if (opts&wDrawOptCursorRmv) color = wDrawColorWhite;   //Wipeout existing cursor draw (simplistic first) + + +	if ((opts&wDrawOptCursor) || (opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) { + +		cairo = CreateCursorSurface((wControl_p)bd,&bd->cursor_surface, bd->w, bd->h, color, opts); + +		if ((opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) { +			bd->cursor_surface.show = FALSE; +		} else bd->cursor_surface.show = TRUE; + +		widget = bd->widget; + + +	} else { +		cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts); +		widget = bd->widget; +	} + +	GtkWidget * new_widget = widget; +	GdkGC * gc = NULL; +	GdkWindow * gdk_window = NULL; + +	win = bd->parent; +#endif +	cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts); +  	for ( i=0; i<bm->w; i++ )  		for ( j=0; j<bm->h; j++ )  			if ( bm->bits[ j*wb+(i>>3) ] & (1<<(i&07)) ) {  				xx = x+i;  				yy = y+j; +#ifdef CURSOR_SURFACE  				if ( 0 <= xx && xx < bd->w &&  					 0 <= yy && yy < bd->h ) { -					gdk_window = bd->pixmap;  					b = (wControl_p)bd;  				} else if ( (opts&wDrawOptNoClip) != 0 ) {  					xx += bd->realX;  					yy += bd->realY;  					b = wlibGetControlFromPos( bd->parent, xx, yy ); -					if ( b ) { -						if ( b->type == B_DRAW ) -							gdk_window = ((wDraw_p)b)->pixmap; -						else -							gdk_window = b->widget->window; +					if ( b) {  						xx -= b->realX;  						yy -= b->realY; +						new_widget = b->widget;  					} else { -						gdk_window = bd->parent->widget->window; +						new_widget = bd->parent->widget;  					}  				} else {  					continue;  				} -/*printf( "gdk_draw_point( %ld, gc, %d, %d )\n", (long)gdk_window, xx, yy );*/ -				//gdk_draw_point( gdk_window, gc, xx, yy ); -				cairo_rectangle(cairo, xx-0.5, yy-0.5, 1, 1); -				cairo_fill(cairo); -				if ( b && b->type == B_DRAW ) { -					update_rect.x = xx-1; -					update_rect.y = yy-1; -					update_rect.width = 3; -					update_rect.height = 3; -					gtk_widget_draw( b->widget, &update_rect ); + +				if (new_widget != widget) { +					if (cairo) +						cairo_destroy(cairo); +					cairo = NULL; +					if (widget && (widget != bd->parent->widget)) +						gtk_widget_queue_draw(GTK_WIDGET(widget)); +					if ( (opts&wDrawOptCursor) || (opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) { +						if (!b) b = (wControl_p)(bd->parent->widget); +						cairo = CreateCursorSurface(b,&b->cursor_surface, b->w, b->h, color, opts); +						widget = b->widget; +						gc = NULL; +						if ((opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) +							b->cursor_surface.show = FALSE; +						else +							b->cursor_surface.show = TRUE; +					} else { +						continue; +					} +					widget = new_widget;  				} -			} -	gtkDrawDestroyCairoContext(cairo); -#ifdef LATER -	gdk_draw_pixmap(bd->pixmap, gc, -		bm->pixmap, -		0, 0, -		x, y, -		bm->w, bm->h ); +				if ((opts&wDrawOptCursorQuit) || (opts&wDrawOptCursorQuit) ) continue;  #endif -	if ( bd->delayUpdate || bd->widget == NULL) return; +				cairo_rectangle(cairo, xx, yy, 1, 1); +				cairo_fill(cairo); +			} + +	cairo_destroy(cairo); + +	if (widget && !bd->delayUpdate) +		gtk_widget_queue_draw_area(GTK_WIDGET(widget), x, y, bm->w, bm->h); -	update_rect.x = x; -	update_rect.y = y; -	update_rect.width = bm->w; -	update_rect.height = bm->h; -	gtk_widget_draw( bd->widget, &update_rect );  } @@ -780,19 +1024,19 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {   void wDrawSaveImage(  		wDraw_p bd )  { +	cairo_t * cr;  	if ( bd->pixmapBackup ) {  		gdk_pixmap_unref( bd->pixmapBackup );  	}  	bd->pixmapBackup = gdk_pixmap_new( bd->widget->window, bd->w, bd->h, -1 ); -	selectGC( bd, 0, wDrawLineSolid, bd->lastColor, 0 ); -	gdk_gc_set_function(bd->gc, GDK_COPY); +	cr = gdk_cairo_create(bd->pixmapBackup); +	gdk_cairo_set_source_pixmap(cr, bd->pixmap, 0, 0); +    cairo_paint(cr); +	cairo_destroy(cr); + +	cr = NULL; -	gdk_draw_pixmap( bd->pixmapBackup, bd->gc, -				bd->pixmap, -				0, 0, -				0, 0, -				bd->w, bd->h );  } @@ -802,14 +1046,13 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  	GdkRectangle update_rect;  	if ( bd->pixmapBackup ) { -		selectGC( bd, 0, wDrawLineSolid, bd->lastColor, 0 ); -		gdk_gc_set_function(bd->gc, GDK_COPY); +		cairo_t * cr; +		cr = gdk_cairo_create(bd->pixmap); +		gdk_cairo_set_source_pixmap(cr, bd->pixmapBackup, 0, 0); +		cairo_paint(cr); +		cairo_destroy(cr); -		gdk_draw_pixmap( bd->pixmap, bd->gc, -				bd->pixmapBackup, -				0, 0, -				0, 0, -				bd->w, bd->h ); +		cr = NULL;  		if ( bd->delayUpdate || bd->widget == NULL ) return;  		update_rect.x = 0; @@ -845,6 +1088,9 @@ static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) {  		if (bd->pixmap)  			gdk_pixmap_unref( bd->pixmap );  		bd->pixmap = gdk_pixmap_new( bd->widget->window, w, h, -1 ); +		if (bd->temp_surface) +			cairo_surface_destroy( bd->temp_surface); +		bd->temp_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, w,h );  		wDrawClear( bd );  		if (!redraw) @@ -914,13 +1160,37 @@ static gint draw_expose_event(  		GdkEventExpose *event,  		wDraw_p bd)  { -	gdk_draw_pixmap(widget->window, -		widget->style->fg_gc[GTK_WIDGET_STATE (widget)], -		bd->pixmap, -		event->area.x, event->area.y, -		event->area.x, event->area.y, -		event->area.width, event->area.height); -	return FALSE; +	static long cDEE = 0; +	if ( iDrawLog ) +		printf( "draw_expose_event %ld %dx%d+%dx%d %dx%d+%dx%d\n", cDEE++, +			event->area.x, event->area.y, event->area.width, event->area.height, +			0, bd->w, 0, bd->h ); + +	cairo_t* cairo = gdk_cairo_create (widget->window); +	gdk_cairo_set_source_pixmap(cairo,bd->pixmap,0,0); +	cairo_rectangle(cairo,event->area.x, event->area.y, +					event->area.width, event->area.height); +	cairo_set_operator(cairo,CAIRO_OPERATOR_SOURCE); +	cairo_fill(cairo); + +	cairo_set_source_surface(cairo,bd->temp_surface,0,0); +	cairo_rectangle(cairo,event->area.x, event->area.y, +				event->area.width, event->area.height); +	cairo_set_operator(cairo,CAIRO_OPERATOR_OVER); +	cairo_fill(cairo); + +#ifdef CURSOR_SURFACE +	if (bd->cursor_surface.surface && bd->cursor_surface.show) { +		cairo_set_source_surface(cairo,bd->cursor_surface.surface,0,0); +		cairo_set_operator(cairo,CAIRO_OPERATOR_OVER); +		cairo_rectangle(cairo,event->area.x, event->area.y, +				       event->area.width, event->area.height); +		cairo_fill(cairo); +	} +#endif +	cairo_destroy(cairo); + +	return TRUE;  } @@ -929,32 +1199,113 @@ static gint draw_configure_event(  		GdkEventConfigure *event,  		wDraw_p bd)  { -	return FALSE; +	return TRUE;  } -static const char * actionNames[] = { "None", "Move", "LDown", "LDrag", "LUp", "RDown", "RDrag", "RUp", "Text", "ExtKey", "WUp", "WDown" }; +static const char * actionNames[] = { "None", "Move", "LDown", "LDrag", "LUp", "RDown", "RDrag", "RUp", "Text", "ExtKey", "WUp", "WDown", "DblL", "ModK", "ScrU", "ScrD", "ScrL", "ScrR" };  /**   * Handler for scroll events, ie mouse wheel activity   */ +static int scrollTimer; +static int timer_busy_count; +static wAction_t lastAction; + +static int ScrollTimerPop(wDraw_p bd) { + +		if (timer_busy_count>1) { +			 timer_busy_count = 0; +			 scrollTimer = 0; +		} else { +			timer_busy_count++; +			return TRUE; +		} +		if (drawVerbose >= 2) +			printf( "%s-Pop\n", actionNames[lastAction] ); +		bd->action( bd, bd->context, lastAction, 0, 0 ); + +		return FALSE; +} + +  static gint draw_scroll_event(  		GtkWidget *widget,  		GdkEventScroll *event,  		wDraw_p bd)  {  	wAction_t action; +	static int oldEventX = 0; +	static int oldEventY = 0; +	static int newEventX = 0; +	static int newEventY = 0; + +	if (event->state & (GDK_SHIFT_MASK|GDK_BUTTON2_MASK|GDK_MOD1_MASK)) { + +		newEventX = OUTMAPX(bd, event->x); +		newEventY = OUTMAPY(bd, event->y); +		oldEventX = OUTMAPX(bd, event->x_root); +		oldEventY = OUTMAPX(bd, event->y_root); + +		switch( event->direction ) { +			case GDK_SCROLL_UP: +				if (event->state & GDK_CONTROL_MASK) +					action = wActionScrollRight; +				else +					action = wActionScrollUp; +				break; +			case GDK_SCROLL_DOWN: +				if (event->state & GDK_CONTROL_MASK) +					action = wActionScrollLeft; +				else +					action = wActionScrollDown; +				break; +			case GDK_SCROLL_LEFT: +				action = wActionScrollLeft; +				break; +			case GDK_SCROLL_RIGHT: +				action = wActionScrollRight; +				break; +			default: +				return TRUE; +				break; +		} -	switch( event->direction ) { -	case GDK_SCROLL_UP: -		action = wActionWheelUp; -		break; -	case GDK_SCROLL_DOWN: -		action = wActionWheelDown; -		break; -	default: -		action = 0; -		break; +		if (drawVerbose >= 2) +			printf( "%sNew[%dx%d]Delta[%dx%d]\n", actionNames[action], +				newEventX, newEventY, oldEventX, oldEventY ); + + + +			if (scrollTimer) {					// Already have a timer +				lastAction = action; +                return TRUE; +            } else { +            	 lastAction = action; +            	 timer_busy_count = 0; +                 scrollTimer = g_timeout_add(25,(GSourceFunc)ScrollTimerPop,bd);   // 25ms delay +                 return TRUE; +            } + + +	} else { + +		switch( event->direction ) { +		case GDK_SCROLL_UP: +			action = wActionWheelUp; +			break; +		case GDK_SCROLL_DOWN: +			action = wActionWheelDown; +			break; +		case GDK_SCROLL_LEFT: +			return TRUE; +			break; +		case GDK_SCROLL_RIGHT: +			return TRUE; +			break; +		default: +			break; +		}  	}  	if (action != 0) { @@ -973,7 +1324,7 @@ static gint draw_leave_event(  		GdkEvent * event )  {  	wlibHelpHideBalloon(); -	return FALSE; +	return TRUE;  } @@ -996,6 +1347,7 @@ static gint draw_button_event(  	switch ( event->button ) {  	case 1: /* left mouse button */  		action = event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp; +		if (event->type==GDK_2BUTTON_PRESS) action = wActionLDownDouble;  		/*bd->action( bd, bd->context, event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp, bd->lastX, bd->lastY );*/  		break;  	case 3: /* right mouse button */ @@ -1050,6 +1402,35 @@ static gint draw_motion_event(  	return TRUE;  } +static gint draw_char_release_event( +		GtkWidget * widget, +		GdkEventKey *event, +		wDraw_p bd ) +{ +		GdkModifierType modifiers; +		guint key = event->keyval; +		wModKey_e modKey = wModKey_None; +		switch (key) { +			case GDK_KEY_Alt_L:     modKey = wModKey_Alt; break; +			case GDK_KEY_Alt_R:     modKey = wModKey_Alt; break; +			case GDK_KEY_Shift_L:	modKey = wModKey_Shift; break; +			case GDK_KEY_Shift_R:	modKey = wModKey_Shift; break; +			case GDK_KEY_Control_L:	modKey = wModKey_Ctrl; break; +			case GDK_KEY_Control_R:	modKey = wModKey_Ctrl; break; +				default: ; +		} + +		if (modKey!= wModKey_None && (bd->option & BD_MODKEYS)) { +			 bd->action(bd, bd->context, wActionModKey+((int)modKey<<8), bd->lastX, bd->lastY ); +			 	 if (!(bd->option & BD_NOFOCUS)) +			 		 gtk_widget_grab_focus( bd->widget ); +			 	 return TRUE; +		} else { +			return FALSE; +		} +		return FALSE; +} +  static gint draw_char_event(  		GtkWidget * widget, @@ -1059,6 +1440,7 @@ static gint draw_char_event(  	GdkModifierType modifiers;  	guint key = event->keyval;  	wAccelKey_e extKey = wAccelKey_None; +	wModKey_e modKey = wModKey_None;  	switch (key) {  	case GDK_KEY_Escape:	key = 0x1B; break;  	case GDK_KEY_Return: @@ -1092,17 +1474,37 @@ static gint draw_char_event(  	case GDK_KEY_F10:       extKey = wAccelKey_F10; break;  	case GDK_KEY_F11:       extKey = wAccelKey_F11; break;  	case GDK_KEY_F12:       extKey = wAccelKey_F12; break; -	default: ; +	case GDK_KEY_Alt_L:     modKey = wModKey_Alt; break; +	case GDK_KEY_Alt_R:     modKey = wModKey_Alt; break; +	case GDK_KEY_Shift_L:	modKey = wModKey_Shift; break; +	case GDK_KEY_Shift_R:	modKey = wModKey_Shift; break; +	case GDK_KEY_Control_L:	modKey = wModKey_Ctrl; break; +	case GDK_KEY_Control_R:	modKey = wModKey_Ctrl; break; +		default: ;  	}  	if (extKey != wAccelKey_None) {  		if ( wlibFindAccelKey( event ) == NULL ) {  			bd->action( bd, bd->context, wActionExtKey + ((int)extKey<<8), bd->lastX, bd->lastY );  		} +		if (!(bd->option & BD_NOFOCUS)) +				gtk_widget_grab_focus( bd->widget ); +		return TRUE; +	} else if ((key >=wAccelKey_Up) && (key<=wAccelKey_Left) && bd->action) { +		bd->action( bd, bd->context, wActionText+(key<<8), bd->lastX, bd->lastY ); +		if (!(bd->option & BD_NOFOCUS)) +			gtk_widget_grab_focus( bd->widget );  		return TRUE;  	} else if (key <= 0xFF && (event->state&(GDK_CONTROL_MASK|GDK_MOD1_MASK)) == 0 && bd->action) {  		bd->action( bd, bd->context, wActionText+(key<<8), bd->lastX, bd->lastY ); +		if (!(bd->option & BD_NOFOCUS)) +				gtk_widget_grab_focus( bd->widget );  		return TRUE; +	} else if (modKey!= wModKey_None && (bd->option & BD_MODKEYS)) { +				bd->action(bd, bd->context, wActionModKey+((int)modKey<<8), bd->lastX, bd->lastY ); +				if (!(bd->option & BD_NOFOCUS)) +								gtk_widget_grab_focus( bd->widget ); +				return TRUE;  	} else {  		return FALSE;  	} @@ -1140,6 +1542,7 @@ int xw, xh, cw, ch;  	bd->context = context;  	bd->redraw = redraw;  	bd->action = action; +	bd->bTempMode = FALSE;  	wlibComputePos( (wControl_p)bd );  	bd->widget = gtk_drawing_area_new(); @@ -1159,6 +1562,8 @@ int xw, xh, cw, ch;  						   (GtkSignalFunc) draw_scroll_event, bd);  	gtk_signal_connect_after (GTK_OBJECT (bd->widget), "key_press_event",  						   (GtkSignalFunc) draw_char_event, bd); +	gtk_signal_connect_after (GTK_OBJECT (bd->widget), "key_release_event", +							   (GtkSignalFunc) draw_char_release_event, bd);  	gtk_signal_connect (GTK_OBJECT (bd->widget), "leave_notify_event",  						   (GtkSignalFunc) draw_leave_event, bd);  	gtk_widget_set_can_focus(bd->widget,!(option & BD_NOFOCUS)); @@ -1168,7 +1573,7 @@ int xw, xh, cw, ch;  							  | GDK_LEAVE_NOTIFY_MASK  							  | GDK_BUTTON_PRESS_MASK  							  | GDK_BUTTON_RELEASE_MASK -/*							  | GDK_SCROLL_MASK */ +							  | GDK_SCROLL_MASK  							  | GDK_POINTER_MOTION_MASK  							  | GDK_POINTER_MOTION_HINT_MASK  							  | GDK_KEY_PRESS_MASK @@ -1182,6 +1587,8 @@ int xw, xh, cw, ch;  	wlibControlGetSize( (wControl_p)bd );  	gtk_widget_realize( bd->widget );  	bd->pixmap = gdk_pixmap_new( bd->widget->window, width, height, -1 ); +	bd->temp_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width,height ); +	wDrawClear(bd);  	bd->gc = gdk_gc_new( parent->gtkwin->window );  	gdk_gc_copy( bd->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] );  { @@ -1219,6 +1626,7 @@ wDraw_p wBitMapCreate(          wPos_t w, wPos_t h, int arg )  	bd->maxH = bd->h = h;  	bd->pixmap = gdk_pixmap_new( gtkMainW->widget->window, w, h, -1 ); +	bd->widget = gtk_pixmap_new(bd->pixmap, NULL);  	if ( bd->pixmap == NULL ) {  		wNoticeEx( NT_ERROR, "CreateBitMap: pixmap_new failed", "Ok", NULL );  		return FALSE; @@ -1242,3 +1650,76 @@ wBool_t wBitMapDelete(          wDraw_p d )  	return TRUE;  } +/******************************************************************************* + * + * Background + * + ******************************************************************************/ +int wDrawSetBackground(    wDraw_p bd, char * path, char ** error) { + +	GError *err = NULL; + +	if (bd->background) { +		g_object_unref(bd->background); +	} + +	if (path) { +		bd->background = gdk_pixbuf_new_from_file (path, &err); +		if (!bd->background) { +			*error = err->message; +			return -1; +		} +	} else { +		bd->background = NULL; +		return 1; +	} +	return 0; + +} + +void wDrawShowBackground( wDraw_p bd, wPos_t pos_x, wPos_t pos_y, wPos_t size, wAngle_t angle, int screen) { + +	if (bd->background) { +		cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, wDrawColorWhite, bd->bTempMode?wDrawOptTemp:0 ); +		cairo_save(cairo); +		int pixels_width = gdk_pixbuf_get_width(bd->background); +		int pixels_height = gdk_pixbuf_get_height(bd->background); +		double scale; +		double posx,posy,width,sized; +		posx = (double)pos_x; +		posy = (double)pos_y; +		if (size == 0) { +			scale = 1.0; +		} else { +			sized = (double)size; +			width = (double)pixels_width; +			scale = sized/width; +		} +		cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); +		double rad = M_PI*(angle/180); +		posy = (double)bd->h-((pixels_height*fabs(cos(rad))+pixels_width*fabs(sin(rad)))*scale)-posy; +		//width = (double)(pixels_width*scale); +		//height = (double)(pixels_height*scale); +		cairo_translate(cairo,posx,posy); +		cairo_scale(cairo, scale, scale); +		cairo_translate(cairo, fabs(pixels_width/2.0*cos(rad))+fabs(pixels_height/2.0*sin(rad)), +				fabs(pixels_width/2.0*sin(rad))+fabs(pixels_height/2.0*cos(rad))); +		cairo_rotate(cairo, M_PI*(angle/180.0)); +		// We need to clip around the image, or cairo will paint garbage data +		cairo_rectangle(cairo, -pixels_width/2.0, -pixels_height/2.0, pixels_width, pixels_height); +		cairo_clip(cairo); +		gdk_cairo_set_source_pixbuf(cairo, bd->background, -pixels_width/2.0, -pixels_height/2.0); +		cairo_pattern_t *mask = cairo_pattern_create_rgba (1.0,1.0,1.0,(100.0-screen)/100.0); +		cairo_mask(cairo,mask); +		cairo_pattern_destroy(mask); +		cairo_restore(cairo); +		gtkDrawDestroyCairoContext(cairo); + +		gtk_widget_queue_draw(bd->widget); +	} + +} + + + + diff --git a/app/wlib/gtklib/gtkint.h b/app/wlib/gtklib/gtkint.h index 2e0511a..410fd7f 100644 --- a/app/wlib/gtklib/gtkint.h +++ b/app/wlib/gtklib/gtkint.h @@ -32,7 +32,9 @@  #define strcasecmp _stricmp  #endif +#ifndef MISC_H  #include "dynarr.h" +#endif  #define BORDERSIZE	(4)  #define LABEL_OFFSET	(3) @@ -40,6 +42,16 @@  extern wWin_p gtkMainW; +#ifdef CURSOR_SURFACE +typedef struct { +		cairo_surface_t* surface; +		wPos_t width; +		wPos_t height; +		wBool_t show; +} wCursorSurface_t, * wSurface_p; +#endif + +  typedef enum {  		W_MAIN, W_POPUP,  		B_BUTTON, B_CANCEL, B_POPUP, B_TEXT, B_INTEGER, B_FLOAT, @@ -59,6 +71,7 @@ typedef void (*setTriggerCallback_p)( wControl_p b );  		wWin_p parent; \  		wPos_t origX, origY; \  		wPos_t realX, realY; \ +		wPos_t default_size_x, default_size_y; \  		wPos_t labelW; \  		wPos_t w, h; \  		int maximize_initially; \ @@ -68,6 +81,8 @@ typedef void (*setTriggerCallback_p)( wControl_p b );  		GtkWidget * widget; \  		GtkWidget * label; \  		doneProcCallback_p doneProc; \ +		/* CURSOR_SURFACE wCursorSurface_t cursor_surface;*/ \ +		wBool_t outline; \  		void * data;  struct wWin_t { @@ -171,6 +186,7 @@ typedef struct {  GdkColor *wlibGetColor(wDrawColor color, wBool_t normal);  /* control.c */ +wBool_t wControlExpose (GtkWidget * widget, GdkEventExpose * event, wControl_p b);  /* droplist.c */  enum columns { @@ -191,7 +207,7 @@ wList_p wDropListCreate(wWin_p parent, wPos_t x, wPos_t y, const char *helpStr,  /* filesel.c */  /* font.c */ -PangoLayout *wlibFontCreatePangoLayout(GtkWidget *widget, void *cairo, wFont_p fp, wFontSize_t fs, const char *s, int *width_p, int *height_p, int *ascent_p, int *descent_p); +PangoLayout *wlibFontCreatePangoLayout(GtkWidget *widget, void *cairo, wFont_p fp, wFontSize_t fs, const char *s, int *width_p, int *height_p, int *ascent_p, int *descent_p, int *baseline_p);  void wlibFontDestroyPangoLayout(PangoLayout *layout);  const char *wlibFontTranslate(wFont_p fp); @@ -255,6 +271,7 @@ struct wDraw_t {  		GdkPixmap * pixmap;  		GdkPixmap * pixmapBackup; +		cairo_surface_t * temp_surface;  		double dpi; @@ -273,6 +290,9 @@ struct wDraw_t {  		wBool_t delayUpdate;  		cairo_t *printContext;  		cairo_surface_t *curPrintSurface; +		GdkPixbuf * background; + +		wBool_t bTempMode;  		};  void WlibApplySettings(GtkPrintOperation *op); @@ -280,7 +300,7 @@ void WlibSaveSettings(GtkPrintOperation *op);  void psPrintLine(wPos_t x0, wPos_t y0, wPos_t x1, wPos_t y1, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts);  void psPrintArc(wPos_t x0, wPos_t y0, wPos_t r, double angle0, double angle1, wBool_t drawCenter, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts);  void psPrintFillRectangle(wPos_t x0, wPos_t y0, wPos_t x1, wPos_t y1, wDrawColor color, wDrawOpts opts); -void psPrintFillPolygon(wPos_t p[][2], int cnt, wDrawColor color, wDrawOpts opts); +void psPrintFillPolygon(wPos_t p[][2], wPolyLine_e type[], int cnt, wDrawColor color, wDrawOpts opts, int fill, int open);  void psPrintFillCircle(wPos_t x0, wPos_t y0, wPos_t r, wDrawColor color, wDrawOpts opts);  void psPrintString(wPos_t x, wPos_t y, double a, char *s, wFont_p fp, double fs, wDrawColor color, wDrawOpts opts);  static void WlibGetPaperSize(void); diff --git a/app/wlib/gtklib/help.c b/app/wlib/gtklib/help.c index dbb69f6..8f2766d 100644 --- a/app/wlib/gtklib/help.c +++ b/app/wlib/gtklib/help.c @@ -28,9 +28,12 @@  #include <gtk/gtk.h>  #include <gdk/gdk.h> +#include "misc.h" +  #include "gtkint.h"  #include "i18n.h" +  /**   * Handle the commands issued from the Help drop-down. Currently, we only have a table   * of contents, but search etc. might be added in the future. @@ -44,11 +47,19 @@ DoHelpMenu(void *data)  {      int func = (intptr_t)data; +    const char * topic; +      switch (func) {      case 1:          wHelp("index");          break; +    case 3: +    	topic = GetCurCommandName(); +    	if (topic && topic[0]) +    		wHelp(topic); +    	break; +      default:          break;      } @@ -56,6 +67,10 @@ DoHelpMenu(void *data)      return;  } +void wDoAccelHelp(wAccelKey_e key, void * context) { +	DoHelpMenu(context); +} +  /**   * Add the entries for Help to the drop-down.   * @@ -66,4 +81,5 @@ DoHelpMenu(void *data)  void wMenuAddHelp(wMenu_p m)  {      wMenuPushCreate(m, NULL, _("&Contents"), 0, DoHelpMenu, (void*)1); +    wMenuPushCreate(m, NULL, _("Co&mmand Context help"), 0, DoHelpMenu, (void*)3);  } diff --git a/app/wlib/gtklib/ixhelp.c b/app/wlib/gtklib/ixhelp.c index f1e3983..5079f61 100644 --- a/app/wlib/gtklib/ixhelp.c +++ b/app/wlib/gtklib/ixhelp.c @@ -404,6 +404,13 @@ void wHelp(const char * topic)  {      char *htmlFile; +    //Take off any topic characters after a '-' + +    if (!topic || !topic[0]) return; + + +    if (!CheckHelpTopicExists(topic)) return; +      if (!wHelpWindow) {          directory = malloc(BUFSIZ);          assert(directory != NULL); @@ -417,6 +424,7 @@ void wHelp(const char * topic)      /* need space for the 'html' extension plus dot plus \0 */      htmlFile = malloc(strlen(topic) + 6); +      assert(htmlFile != NULL);      sprintf(htmlFile, "%s.html", topic); @@ -424,4 +432,6 @@ void wHelp(const char * topic)      load_into_view(htmlFile, MAIN_VIEW);      gtk_widget_show_all(wHelpWindow);      gtk_window_present(GTK_WINDOW(wHelpWindow)); + +    free(htmlFile);  } diff --git a/app/wlib/gtklib/list.c b/app/wlib/gtklib/list.c index 8e99efe..ac66aba 100644 --- a/app/wlib/gtklib/list.c +++ b/app/wlib/gtklib/list.c @@ -238,6 +238,7 @@ wIndex_t wListGetValues(      if (bl->type == B_DROPLIST && bl->editted) {          entry_value = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(                                               bl->widget)))); +	item_data = NULL;          inx = bl->last = -1;      } else {          inx = bl->last; @@ -404,6 +405,8 @@ void wListDelete(                                        NULL,                                        inx);          gtk_list_store_remove(b->listStore, &iter); + +          b->count--;      } @@ -486,7 +489,7 @@ wIndex_t wListAddValue(          wlibTreeViewAddRow(b, (char *)labelStr, bm, id_p);      } -    free(id_p->label); +    //free(id_p->label);      b->count++;      b->recursion--; diff --git a/app/wlib/gtklib/liststore.c b/app/wlib/gtklib/liststore.c index 820366a..088bf33 100644 --- a/app/wlib/gtklib/liststore.c +++ b/app/wlib/gtklib/liststore.c @@ -127,6 +127,8 @@ wlibListStoreClear(GtkListStore *listStore)      id_p = wlibListStoreGetContext(listStore, i++);      while (id_p) { +        if (id_p->label) +            g_free(id_p->label);          g_free(id_p);          id_p = wlibListStoreGetContext(listStore, i++);      } diff --git a/app/wlib/gtklib/menu.c b/app/wlib/gtklib/menu.c index d19805a..79695d4 100644 --- a/app/wlib/gtklib/menu.c +++ b/app/wlib/gtklib/menu.c @@ -555,7 +555,7 @@ wMenuList_p wMenuListCreate(   * behind it. In case the maximum number of items is reached the last item is removed.   *   * \param ml 		IN handle for the menu list - the placeholder item - * \param index 	IN currently ignored + * \param index 	IN position of new menu item   * \param labelStr 	IN the menu label for the new item    * \param data 		IN application data for the new item   * \return     @@ -611,7 +611,11 @@ void wMenuListAdd(  		g_object_set_data( G_OBJECT(MMENUITEM( mi )), WLISTITEM,  mi );  		// add the item to the menu -		gtk_menu_shell_insert((GtkMenuShell *)(MPARENT( ml )->menu), MMENUITEM( mi ), i + 1 ); +		if ( index < 0 ) +			index = 0; +		if ( index >= ml->count ) +			index = ml->count - 1; +		gtk_menu_shell_insert((GtkMenuShell *)(MPARENT( ml )->menu), MMENUITEM( mi ), i + index + 1 );  		g_signal_connect( GTK_OBJECT(MMENUITEM( mi )), "activate", G_CALLBACK(pushMenuList), mi );  		gtk_widget_show(MMENUITEM( mi )); diff --git a/app/wlib/gtklib/notice.c b/app/wlib/gtklib/notice.c index b72afd6..0134b4f 100644 --- a/app/wlib/gtklib/notice.c +++ b/app/wlib/gtklib/notice.c @@ -58,8 +58,13 @@ static void doNotice(      GtkWidget * widget,      long value)  { -    noticeValue = value; -    gtk_widget_destroy(noticeW.win); +    if (value != 2) { +	// event not from from closing the window but from a button press +	// Close the Notice dialog +    	gtk_widget_destroy(noticeW.win); +	// Remember the button +        noticeValue = value; +    }      wlibDoModal(NULL, FALSE);  } @@ -106,6 +111,8 @@ int wNoticeEx(int type,          parent = GTK_WINDOW(gtkMainW->gtkwin);      } +    wDestroySplash(); +      dialog = gtk_message_dialog_new(parent,                                      GTK_DIALOG_DESTROY_WITH_PARENT,                                      flag, @@ -113,6 +120,8 @@ int wNoticeEx(int type,                                      "%s", msg);      gtk_window_set_title(GTK_WINDOW(dialog), headline); +    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); +      res = gtk_dialog_run(GTK_DIALOG(dialog));      gtk_widget_destroy(dialog); @@ -174,6 +183,7 @@ int wNotice3(      char *can = NULL;      char *alt = NULL; +    wDestroySplash();      nw->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -243,6 +253,9 @@ int wNotice3(          }      } +    g_signal_connect(GTK_WINDOW(nw->win), +            "destroy", G_CALLBACK(doNotice), (void*)2); +      gtk_widget_grab_default(nw->butt[ 0 ]);      gtk_widget_grab_focus(nw->butt[ 0 ]); @@ -253,6 +266,7 @@ int wNotice3(          /*		gdk_window_set_group( nw->win->window, gtkMainW->gtkwin->window ); */      } +    noticeValue = 0; // Default: Cancel      wlibDoModal(NULL, TRUE);      if (aff) { diff --git a/app/wlib/gtklib/opendocument.c b/app/wlib/gtklib/opendocument.c new file mode 100644 index 0000000..c03f9cb --- /dev/null +++ b/app/wlib/gtklib/opendocument.c @@ -0,0 +1,117 @@ +/** \file opendocument.c + * open a document using the systems default application for that doc + */ + +/*  XTrkCad - Model Railroad CAD + *  Copyright (C) 2018 Martin Fischer + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include "gtkint.h" +#include "i18n.h" + +#include "dynstring.h" + +#if defined (_WIN32) + +#define DEFAULTOPENCOMMAND "start" + +#endif + +#if defined(__APPLE__) && defined(__MACH__) + +#define DEFAULTOPENCOMMAND "open" + +#else + +#define DEFAULTOPENCOMMAND "xdg-open" + +#endif + +/** + * Extend the PATH variable in the environment to include XTrackCAD's + * script directory. + * + * \return pointer to old path + */ + +static char * +ExtendPath(void) +{ +    char *path = strdup(getenv("PATH")); +    DynString newPath; +    DynStringMalloc(&newPath, 16); + +    // append XTrackCAD's directory to the path as a fallback +    DynStringCatCStrs(&newPath, +                      path, +                      ":", +                      wGetAppLibDir(), +                      NULL); + +    setenv("PATH", +           DynStringToCStr(&newPath), +           TRUE); + +    DynStringFree(&newPath); + +    return (path); +} + +/** + * Invoke the system's default application to open a file. First the + * system's standard xdg-open command is attempted. If that is not available, the + * version included with the XTrackCAD installation is executed. + * + * \param topic IN URI of document + */ + +unsigned wOpenFileExternal(char * filename) +{ +    int rc; +    DynString commandLine; +    char *currentPath; + +    assert(filename != NULL); +    assert(strlen(filename)); + +    currentPath = ExtendPath(); +     +    DynStringMalloc(&commandLine, 16); +    DynStringCatCStrs(&commandLine, +                      DEFAULTOPENCOMMAND, +                      " \"", +                      filename, +					  "\"", +                      NULL); + +    // the command should be found via the PATH +    rc = system(DynStringToCStr(&commandLine)); + +    // restore the PATH +    setenv("PATH", +           currentPath, +           TRUE); + +    free(currentPath); +    DynStringFree(&commandLine); +     +    return(rc==0); +} diff --git a/app/wlib/gtklib/osxhelp.c b/app/wlib/gtklib/osxhelp.c index 829ec94..4ec1f5e 100644 --- a/app/wlib/gtklib/osxhelp.c +++ b/app/wlib/gtklib/osxhelp.c @@ -28,6 +28,7 @@  #include <errno.h>  #include <fcntl.h> +#include "misc.h"  #include "gtkint.h"  #include "i18n.h" @@ -39,6 +40,7 @@ static pid_t pidOfChild;  static int handleOfPipe;  extern char *wExecutableName; +  /**   * Create the fully qualified filename for the help helper   * @@ -70,14 +72,21 @@ char *ChildProgramFile(char *parentProgram)  void wHelp(const char * topic)  {      pid_t newPid; -    int len;      int status;      const char html[] = ".html"; +    static char *directory;				/**< base directory for HTML files */ +    char * htmlFile; + + struct { +    int length;      char *page; -     + } buffer; + + 	if (!CheckHelpTopicExists(topic)) return; +      // check whether child already exists      if (pidOfChild != 0) { -        if (waitpid(pidOfChild, &status, WNOHANG) > 0) { +        if (waitpid(pidOfChild, &status, WNOHANG) < 0) {              // child exited -> clean up              close(handleOfPipe);              unlink(HELPCOMMANDPIPE); @@ -88,7 +97,8 @@ void wHelp(const char * topic)      // (re)start child      if (pidOfChild == 0) { -        mkfifo(HELPCOMMANDPIPE, 0666); +    	unlink(HELPCOMMANDPIPE); +        int rc = mkfifo(HELPCOMMANDPIPE, 0666);          newPid = fork();  /* New process starts here */          if (newPid > 0) { @@ -107,27 +117,46 @@ void wHelp(const char * topic)          }      } -    if (!handleOfPipe) { -        handleOfPipe = open(HELPCOMMANDPIPE, O_WRONLY); - -        if (handleOfPipe < 0) { -            kill(pidOfChild, SIGKILL);  /* tidy up on next call */ -        } +    buffer.page = malloc(sizeof(int)+strlen(topic) + strlen(html) + 1); +    if (!buffer.page) { +        return;      } -    page = malloc(strlen(topic) + strlen(html) + 1); +    strcpy(buffer.page, topic); +    strcat(buffer.page, html); +    buffer.length = strlen(buffer.page); -    if (!page) { -        return; +    if (buffer.length>255) { +    	printf("Help Topic too long %s", buffer.page); +    	return;      } -    strcpy(page, topic); -    strcat(page, html); -    len = strlen(page); +    if (!handleOfPipe) { +		handleOfPipe = open(HELPCOMMANDPIPE, O_WRONLY); + +		if (handleOfPipe < 0) { +			if (pidOfChild) +				kill(pidOfChild, SIGKILL);  /* tidy up on next call */ +			handleOfPipe = 0; +			return; +		} + +	} + +    int written = 0; +    int towrite = sizeof(int); + +    while (written < towrite){ +    	written += write(handleOfPipe, &buffer.length, sizeof(int)); +    } +    written =0; +    towrite = strlen(buffer.page); +    while (written < towrite){ +        written += write(handleOfPipe, buffer.page+written, towrite-written); +    } -    write(handleOfPipe, &len, sizeof(int)); -    write(handleOfPipe, page, strlen(page)+1); +    fsync(handleOfPipe); -    free(page); +    free(buffer.page);  } diff --git a/app/wlib/gtklib/print.c b/app/wlib/gtklib/print.c index 8e96e3b..860a7c7 100644 --- a/app/wlib/gtklib/print.c +++ b/app/wlib/gtklib/print.c @@ -70,9 +70,9 @@ extern wDrawColor wDrawColorBlack;   *   */ -static GtkPrintSettings *settings;			/**< current printer settings */ +static GtkPrintSettings *settings = NULL;			/**< current printer settings */  static GtkPageSetup *page_setup;			/**< current paper settings */ -static GtkPrinter *selPrinter;				/**< printer selected by user */ +static GtkPrinter *selPrinter = NULL;				/**< printer selected by user */  static GtkPrintJob *curPrintJob;			/**< currently active print job */  extern struct wDraw_t psPrint_d; @@ -131,7 +131,6 @@ WlibApplySettings(GtkPrintOperation *op)              // create  default print settings              settings = gtk_print_settings_new();          } -          g_error_free(err);      } @@ -247,12 +246,13 @@ void wPrintSetup(wPrintSetupCallBack_p callback)      GError *err;      GtkWidget *dialog; -    WlibApplySettings(NULL); +    if ( !settings ) +        WlibApplySettings(NULL);      new_page_setup = gtk_print_run_page_setup_dialog(GTK_WINDOW(gtkMainW->gtkwin),                       page_setup, settings); -    if (page_setup) { +    if (page_setup && (page_setup != new_page_setup)) {      //Can be the same if no mods...          g_object_unref(page_setup);      } @@ -264,6 +264,51 @@ void wPrintSetup(wPrintSetupCallBack_p callback)  /*****************************************************************************   * + *  + * + */ + + +static GtkPrinter * pDefaultPrinter = NULL; +gboolean isDefaultPrinter( GtkPrinter * printer, gpointer data ) +{ +const char * pPrinterName = gtk_printer_get_name( printer ); +	if ( gtk_printer_is_default( printer ) ) { +		pDefaultPrinter = printer; +		return TRUE; +	} +	return FALSE; +} + +static void getDefaultPrinter() +{ +	pDefaultPrinter = NULL; +	gtk_enumerate_printers( isDefaultPrinter, NULL, NULL, TRUE ); +}  + +const char * wPrintGetName() +{ +	static char sPrinterName[100]; +	WlibApplySettings( NULL ); +	const char * pPrinterName =  +		gtk_print_settings_get( settings, "format-for-printer" ); +	if ( pPrinterName == NULL ) { +		getDefaultPrinter(); +		if ( pDefaultPrinter ) +			pPrinterName = gtk_printer_get_name( pDefaultPrinter ); +	} +	if ( pPrinterName == NULL ) { +		pPrinterName = ""; +	} +	strncpy (sPrinterName, pPrinterName, sizeof sPrinterName - 1 ); +	sPrinterName[ sizeof sPrinterName - 1 ] = '\0'; +	for ( char * cp = sPrinterName; *cp; cp++ ) +		if ( *cp == ':' ) +			*cp = '-'; +	return sPrinterName; +} +/***************************************************************************** + *   * BASIC PRINTING   *   */ @@ -299,12 +344,53 @@ static void setLineType(      }      cairo_set_line_width(cr, lineWidth); - -    if (lineType == wDrawLineDash) { -        cairo_set_dash(cr, dashes, len_dashes, 0.0); -    } else { -        cairo_set_dash(cr, NULL, 0, 0.0); +    switch(lineType) { +    	case wDrawLineDot: +    	{ +    		double dashes[] = { 1,  2 , 1,  2}; +    	    static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +    	    cairo_set_dash(cr, dashes, len_dashes, 0.0); +    	    break; +    	} +    	case wDrawLineDash: +    	{ +    		double dashes[] = { DASH_LENGTH, 3 };							//Reduce gap in between dashes +    		static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +    		cairo_set_dash(cr, dashes, len_dashes, 0.0); +			break; +    	} +    	case wDrawLineDashDot: +    	{ +    		double dashes[] = { 3, 2, 1, 2}; +    		static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +    		cairo_set_dash(cr, dashes, len_dashes, 0.0); +    		break; +    	} +    	case wDrawLineDashDotDot: +    	{ +    		double dashes[] = { 3, 2, 1, 2, 1, 2}; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cr, dashes, len_dashes, 0.0); +			break; +    	} +    	case wDrawLineCenter: +		{ +			double dashes[] = { 1.5*DASH_LENGTH, 3, DASH_LENGTH, 3}; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cr, dashes, len_dashes, 0.0); +			break; +		} +    	case wDrawLinePhantom: +		{ +			double dashes[] = { 1.5*DASH_LENGTH, 3, DASH_LENGTH, 3, DASH_LENGTH, 3}; +			static int len_dashes  = sizeof(dashes) / sizeof(dashes[0]); +			cairo_set_dash(cr, dashes, len_dashes, 0.0); +			break; +		} +    	default: +    		cairo_set_dash(cr, NULL, 0, 0.0);      } +  }  /** @@ -479,14 +565,18 @@ void psPrintFillRectangle(   * \param cnt IN the number of points   * \param color IN fill color   * \param opts IN options + * \paran fill IN Fill or not   * \return   */  void psPrintFillPolygon(      wPos_t p[][2], +	wPolyLine_e type[],      int cnt,      wDrawColor color, -    wDrawOpts opts) +    wDrawOpts opts, +	int fill, +	int open )  {      int inx;      cairo_t *cr = psPrint_d.printContext; @@ -501,13 +591,67 @@ void psPrintFillPolygon(      psSetColor(color); -    cairo_move_to(cr, p[ 0 ][ 0 ], p[ 0 ][ 1 ]); +    wPos_t mid0[2], mid1[2], mid2[2], mid3[2], mid4[2];      for (inx=0; inx<cnt; inx++) { -        cairo_line_to(cr, p[ inx ][ 0 ], p[ inx ][ 1 ]); +    	int j = inx-1; +    	int k = inx+1; +    	if (j < 0) j = cnt-1; +    	if (k > cnt-1) k = 0; +		double len0, len1; +		double d0x = (p[inx][0]-p[j][0]); +		double d0y = (p[inx][1]-p[j][1]); +		double d1x = (p[k][0]-p[inx][0]); +		double d1y = (p[k][1]-p[inx][1]); +		len0 = (d0x*d0x+d0y*d0y); +		len1 = (d1x*d1x+d1y*d1y); +		mid0[0] = (d0x/2)+p[j][0]; +		mid0[1] = (d0y/2)+p[j][1]; +		mid1[0] = (d1x/2)+p[inx][0]; +		mid1[1] = (d1y/2)+p[inx][1]; +		if (type && (type[inx] == wPolyLineRound) && (len1>0) && (len0>0)) { +			double ratio = sqrt(len0/len1); +			if (len0 < len1) { +				mid1[0] = ((d1x*ratio)/2)+p[inx][0]; +				mid1[1] = ((d1y*ratio)/2)+p[inx][1]; +			} else { +				mid0[0] = p[inx][0]-(d0x/(2*ratio)); +				mid0[1] = p[inx][1]-(d0y/(2*ratio)); +			} +		} +		mid3[0] = (p[inx][0]-mid0[0])/2+mid0[0]; +		mid3[1] = (p[inx][1]-mid0[1])/2+mid0[1]; +		mid4[0] = (mid1[0]-p[inx][0])/2+p[inx][0]; +		mid4[1] = (mid1[1]-p[inx][1])/2+p[inx][1]; +		wPos_t save[2]; +		if (inx==0) { +			 if (!type || (type && type[0] == wPolyLineStraight) || open) { +				 cairo_move_to(cr, p[ 0 ][ 0 ], p[ 0 ][ 1 ]); +				 save[0] = p[0][0]; save[1] = p[0][1]; +			 } else { +				 cairo_move_to(cr, mid0[0], mid0[1]); +				 if (type[inx] == wPolyLineSmooth) +				 	cairo_curve_to(cr, p[inx][0], p[inx][1], p[inx][0], p[inx][1], mid1[0], mid1[1]); +				 else +				 	cairo_curve_to(cr, mid3[0], mid3[1], mid4[0], mid4[1], mid1[0], mid1[1]); +				 save[0] = mid0[0]; save[1] = mid0[1]; +			 } +		} else if (!type || (type && type[inx] == wPolyLineStraight) || (open && (inx==cnt-1)) ) { +			cairo_line_to(cr, p[ inx ][ 0 ], p[ inx ][ 1 ]); +		} else { +			cairo_line_to(cr, mid0[ 0 ], mid0[ 1 ]); +			if (type && type[inx] == wPolyLineSmooth) +				cairo_curve_to(cr, p[inx][0],p[inx][1],p[inx][0],p[inx][1],mid1[0],mid1[1]); +			else +				cairo_curve_to(cr, mid3[0],mid3[1],mid4[0],mid4[1],mid1[0],mid1[1]); +		} +		if ((inx==cnt-1) && !open) { +			cairo_line_to(cr, save[0], save[1]); +		}      } -    cairo_fill(cr); +    if (fill && !open) cairo_fill(cr); +    else cairo_stroke(cr);  }  /** @@ -602,6 +746,7 @@ void psPrintString(      cairo_matrix_transform_point(&matrix, &x0, &y0); +    cairo_identity_matrix(cr);      layout = pango_cairo_create_layout(cr); @@ -609,7 +754,6 @@ void psPrintString(      /** \todo use a getter function instead of double conversion */      desc = pango_font_description_from_string(wlibFontTranslate(fp)); -      pango_font_description_set_size(desc, fs * PANGO_SCALE * scale_text);      // render the string to a Pango layout @@ -617,28 +761,41 @@ void psPrintString(      pango_layout_set_text(layout, s, -1);      pango_layout_set_width(layout, -1);      pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); -    pango_layout_get_pixel_size(layout, &text_width, &text_height); +    pango_layout_get_size(layout, &text_width, &text_height); + +    text_width = text_width / PANGO_SCALE; +    text_height = text_height / PANGO_SCALE;      // get the height of the string      pcontext = pango_cairo_create_context(cr);      metrics = pango_context_get_metrics(pcontext, desc,                                          pango_context_get_language(pcontext)); -    ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE *scale_adjust; - -    cairo_identity_matrix(cr); +    ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; -    cairo_translate(cr, x0 + ((ascent + (bBorder*scale_adjust)) * sin(-a * M_PI / 180.0))+((lBorder*scale_adjust)* cos(a * M_PI / 180.0)), -    					y0 - ((ascent + (bBorder*scale_adjust)) * cos( a * M_PI / 180.0))+((lBorder*scale_adjust)* sin(a * M_PI / 180.0))); +    int baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; +    cairo_translate(cr, x0,	y0 );      cairo_rotate(cr, -a * M_PI / 180.0); +    cairo_translate( cr, 0, -baseline ); + +    cairo_move_to(cr,0,0); + +    pango_cairo_update_layout(cr, layout); +      // set the color      psSetColor(color);      // and show the string -    pango_cairo_show_layout(cr, layout); - +    if(!(opts & wDrawOutlineFont)) { +		pango_cairo_show_layout(cr, layout); +	} else { +		PangoLayoutLine *line; +		line = pango_layout_get_line_readonly (layout, 0); +		pango_cairo_layout_line_path (cr, line); +		cairo_stroke( cr );	 +	}      // free unused objects      g_object_unref(layout);      g_object_unref(pcontext); @@ -704,21 +861,20 @@ WlibGetPaperSize(void)   * \return   */ -void wPrintGetPageSize( -    double * w, -    double * h) -{ -    // if necessary load the settings -    if (!settings) { -        WlibApplySettings(NULL); -    } - -    WlibGetPaperSize(); -    *w = paperWidth -lBorder - rBorder; -    *h = paperHeight - tBorder - bBorder; +void wPrintGetMargins( +	double * tMargin, +	double * rMargin, +	double * bMargin, +	double * lMargin ) +{ +	if ( tMargin ) *tMargin = tBorder; +	if ( rMargin ) *rMargin = rBorder; +	if ( bMargin ) *bMargin = bBorder; +	if ( lMargin ) *lMargin = lBorder;  } +  /**   * Get the paper size. The size returned is the physical size of the   * currently selected paper. @@ -727,7 +883,7 @@ void wPrintGetPageSize(   * \return   */ -void wPrintGetPhysSize( +void wPrintGetPageSize(      double * w,      double * h)  { @@ -854,13 +1010,13 @@ wBool_t wPrintDocStart(const char * title, int fTotalPageCount, int * copiesP)                                      NULL);          psPrint_d.printContext = cairo_create(psPrint_d.curPrintSurface); +        WlibApplySettings( NULL );          //update the paper dimensions          WlibGetPaperSize();          /* for all surfaces including files the resolution is always 72 ppi (as all GTK uses PDF) */          surface_type = cairo_surface_get_type(psPrint_d.curPrintSurface); -        const char * printer_name = gtk_print_settings_get_printer(settings);          /*           * Override up-scaling for some printer drivers/Linux systems that don't support the latest CUPS           * - the user sets the environment variable XTRKCADPRINTSCALE to a value @@ -871,7 +1027,8 @@ wBool_t wPrintDocStart(const char * title, int fTotalPageCount, int * copiesP)           */          char * sEnvScale = PRODUCT "PRINTSCALE"; -        if ((strcmp(printer_name,"Print to File") == 0) || getenv(sEnvScale) == NULL) { +	const char * sPrinterName = gtk_printer_get_name( selPrinter ); +        if ((strcmp(sPrinterName,"Print to File") == 0) || getenv(sEnvScale) == NULL) {  			double p_def = 600;  			cairo_surface_set_fallback_resolution(psPrint_d.curPrintSurface, p_def, p_def);  			psPrint_d.dpi = p_def; diff --git a/app/wlib/gtklib/single.c b/app/wlib/gtklib/single.c index 45ed6e4..600f1dd 100644 --- a/app/wlib/gtklib/single.c +++ b/app/wlib/gtklib/single.c @@ -50,8 +50,10 @@ struct wString_t {  	char *valueP;			/**< pointer to result buffer */  	wIndex_t valueL;	 	/**< maximum length */  	wStringCallBack_p action;  	/**< callback for changes */ -	wBool_t busy;		 	/**< busy flag to prevent re-entry problems? */	 +	wBool_t notice_activate; /** if flag set to observe enter key **/ +	wBool_t enter_pressed;	/**< flag if enter was pressed */  	wBool_t hasSignal;		/** needs signal to be suppressed */ +	int count;				/** number of 100ms since last entry **/  	guint	timer;			/**< timer source for inactivity timer */  }; @@ -142,33 +144,43 @@ static gboolean killTimer(  }	  /** - *	Timer handler for string activity. This timer expires if the user - * 	doesn't change an entry value within the preset time.  + *	Timer handler for string activity. This timer checks the input if the user + * 	doesn't change an entry value for the preset time (0.5s).   */  static gboolean  timeoutString( wString_p bs )   { - +	const char *new_value;  	if ( !bs )  		return( FALSE );  	if (bs->widget == 0)   		abort(); -	if (bs->action) { -		const char *s; -		 -		s = gtk_entry_get_text(GTK_ENTRY(bs->widget)); -		if ( s ) -			bs->action(s, bs->data); +	bs->count--; + +	if (bs->count==0) { +		// get the currently entered value +	    new_value = wStringGetValue(bs); +		if (bs->valueP != NULL) +			strcpy(bs->valueP, new_value); + +		if (bs->action) { +			bs->enter_pressed = FALSE;     //Normal input +			if ( new_value ) +				bs->action(new_value,bs->data); +		} +	} +	if (bs->count<=0) { +		bs->timer = 0; +		return( FALSE );   //Stop timer +	} else { +		return TRUE;       //Wait 100ms  	} - -	bs->timer = 0; -	return( FALSE );  }  /** - * Signal handler for 'activate' signal: callback with the current value and then  + * Signal handler for 'activate' signal: enter pressed - callback with the current value and then   * select the whole default value   *   * \param widget 	IN the edit field @@ -181,6 +193,7 @@ static gboolean stringActivated(      wString_p b)   {  	const char *s; +	const char * output = "\n";  	if ( !b )  		return( FALSE ); @@ -191,14 +204,22 @@ static gboolean stringActivated(  		strcpy(b->valueP, s);  	if (b->action) { -		b->action(s, b->data); +		b->enter_pressed = TRUE; +		b->action( output, b->data);  	}  	// select the complete default value to make editing it easier  	gtk_editable_select_region( GTK_EDITABLE( widget ), 0, -1 ); -	return( FALSE ); +	return( TRUE ); +} + +static gboolean stringExposed(GtkWidget* widget, GdkEventExpose * event, gpointer g ) +{ +	wControl_p b = (wControl_p)g; +	return wControlExpose(widget,event,b);  } +  /**   * Signal handler for changes in an entry field   * @@ -213,25 +234,27 @@ static void stringChanged(  {  	const char *new_value; -	if ( !b || b->busy ) +	if ( !b  )  		return; +	b->count = 5;              /* set ~500 ms from now */ +  	// get the entered value -	new_value = wStringGetValue(b); -	if (b->valueP != NULL) -		strcpy(b->valueP, new_value); -	 +	//new_value = wStringGetValue(b); +	//if (b->valueP != NULL) +	//	strcpy(b->valueP, new_value); +	//  	//   	if (b->action){  		// if one exists, remove the inactivity timer -		if( b->timer ) -			g_source_remove( b->timer ); +		if( !b->timer ) { +			//g_source_remove( b->timer );  		// create a new timer -		b->timer = g_timeout_add( TIMEOUT_INACTIVITY, +			b->timer = g_timeout_add( TIMEOUT_INACTIVITY/5,  								  (GSourceFunc)timeoutString,  -								  b ); -		 +								  	  b ); +		}  	}	  	return;  } @@ -318,9 +341,12 @@ wString_p wStringCreate(  	// link into help   	wlibAddHelpString(b->widget, helpStr); -	g_signal_connect(GTK_OBJECT(b->widget), "changed", G_CALLBACK(stringChanged), b); -	//g_signal_connect(GTK_OBJECT(b->widget), "activate", G_CALLBACK(stringActivated), b); +	//g_signal_connect(GTK_OBJECT(b->widget), "changed", G_CALLBACK(stringChanged), b); +	//if (option&BO_ENTER) +		g_signal_connect(GTK_OBJECT(b->widget), "activate", G_CALLBACK(stringActivated), b);  	b->hasSignal = 1; +		g_signal_connect_after(GTK_OBJECT(b->widget), "expose-event", +	    							G_CALLBACK(stringExposed), b);  	// set the default text	and select it to make replacing it easier  	if (b->valueP) { diff --git a/app/wlib/gtklib/splash.c b/app/wlib/gtklib/splash.c index 0d5be50..5d56e9f 100644 --- a/app/wlib/gtklib/splash.c +++ b/app/wlib/gtklib/splash.c @@ -63,7 +63,7 @@ wCreateSplash(char *appName, char *appVer)      gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);      gtk_window_set_resizable(GTK_WINDOW(window), FALSE);      gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN); -#if GTK_MINOR_VERSION > 5 +#if GTK_MAJOR_VERSION > 1 || GTK_MINOR_VERSION > 5      gtk_window_set_focus_on_map(GTK_WINDOW(window), FALSE);  #endif @@ -108,7 +108,6 @@ wCreateSplash(char *appName, char *appVer)      message = label;      gtk_widget_show(window); -      return (TRUE);  } @@ -121,8 +120,9 @@ wCreateSplash(char *appName, char *appVer)  int  wSetSplashInfo(char *msg)  { -    if (msg) { -        gtk_label_set_text((GtkLabel *)message, msg); +	if (!window) return FALSE; +    if (msg && message) { +        gtk_label_set_text(GTK_LABEL(message), msg);          wFlush();          return TRUE;      } @@ -139,6 +139,8 @@ void  wDestroySplash(void)  {      /* kill window */ -    gtk_widget_destroy(window); +    if (window) gtk_widget_destroy(window); +    window = NULL; +      return;  } diff --git a/app/wlib/gtklib/statusbar.c b/app/wlib/gtklib/statusbar.c index 3730eab..3a2fd0d 100644 --- a/app/wlib/gtklib/statusbar.c +++ b/app/wlib/gtklib/statusbar.c @@ -67,6 +67,7 @@ void wStatusSetValue(      }      gtk_entry_set_text(GTK_ENTRY(b->labelWidget), wlibConvertInput(arg)); +    gtk_widget_queue_draw (GTK_WIDGET(b->labelWidget));  }  /**   * Create a window for a simple text. @@ -99,6 +100,9 @@ wStatus_p wStatusCreate(      gtk_editable_set_editable(GTK_EDITABLE(b->labelWidget), FALSE);      gtk_entry_set_has_frame(GTK_ENTRY(b->labelWidget), FALSE);      gtk_widget_set_can_focus(b->labelWidget, FALSE); +    gtk_widget_set_sensitive(b->labelWidget, FALSE); +    GdkColor black = {0, 0x0000, 0x0000, 0x0000}; +    gtk_widget_modify_text(b->labelWidget,GTK_STATE_INSENSITIVE,&black);      gtk_entry_set_text(GTK_ENTRY(b->labelWidget),                         message?wlibConvertInput(message):""); @@ -138,7 +142,7 @@ wStatusGetWidth(const char *testString)      gtk_widget_destroy(entry);      g_object_unref(entry); -    return (requisition.width+8); +    return (requisition.width);  }  /** diff --git a/app/wlib/gtklib/text.c b/app/wlib/gtklib/text.c index f7ba288..0812ace 100644 --- a/app/wlib/gtklib/text.c +++ b/app/wlib/gtklib/text.c @@ -98,6 +98,8 @@ void wTextAppend(wText_p bt,  {      GtkTextBuffer *tb;      GtkTextIter ti1; +    GtkTextMark *tm; +          if (bt->text == 0) {          abort(); @@ -109,6 +111,18 @@ void wTextAppend(wText_p bt,      // append to end of buffer      gtk_text_buffer_get_end_iter(tb, &ti1);      gtk_text_buffer_insert(tb, &ti1, text, -1); +     +    if ( bt->option & BT_TOP ) { +        // and scroll to start of text +        gtk_text_buffer_get_start_iter(tb, &ti1); +    } else { +        // and scroll to end of text +        gtk_text_buffer_get_end_iter(tb, &ti1); +    } +    tm = gtk_text_buffer_create_mark(tb, NULL, &ti1, TRUE ); +    gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW(bt->text), tm ); +    gtk_text_buffer_delete_mark( tb, tm ); +       bt->changed = FALSE;  } @@ -116,7 +130,7 @@ void wTextAppend(wText_p bt,   * Get the text from a text buffer in system codepage   * The caller is responsible for free'ing the allocated storage.   * - * \todo handling of return from gtkConvertOutput can be improved + * Dont convert from UTF8   *   * \param bt IN the text widget   * \return    pointer to the converted text @@ -135,8 +149,8 @@ static char *wlibGetText(wText_p bt)      tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bt->text));      gtk_text_buffer_get_bounds(tb, &ti1, &ti2);      cp = gtk_text_buffer_get_text(tb, &ti1, &ti2, FALSE); -    cp1 = wlibConvertOutput(cp); -    res = strdup(cp1); +    //cp1 = wlibConvertOutput(cp); +    res = strdup(cp);      g_free(cp);      return res;  } @@ -375,7 +389,7 @@ wBool_t wTextPrint(   * Get the length of text   *   * \param bt IN the text widget - * \return    length of string + * \return    length of string including terminating \0   */  int wTextGetSize(wText_p bt) @@ -383,7 +397,7 @@ int wTextGetSize(wText_p bt)      char *cp = wlibGetText(bt);      int len = strlen(cp);      free(cp); -    return len; +    return len + 1;  }  /** diff --git a/app/wlib/gtklib/timer.c b/app/wlib/gtklib/timer.c index 812908f..80c71fb 100644 --- a/app/wlib/gtklib/timer.c +++ b/app/wlib/gtklib/timer.c @@ -109,6 +109,9 @@ void wlibSetTrigger(  void wPause(      long count)		/* milliseconds */  { +	while (gtk_events_pending()) +	    gtk_main_iteration();			//Allow GTK to finish before pausing +      struct timeval timeout;      sigset_t signal_mask;      sigset_t oldsignal_mask; diff --git a/app/wlib/gtklib/util.c b/app/wlib/gtklib/util.c index e6587a0..a265938 100644 --- a/app/wlib/gtklib/util.c +++ b/app/wlib/gtklib/util.c @@ -197,6 +197,8 @@ void * wlibAlloc(          abort();      } +    w->outline = FALSE; +      w->type = type;      w->parent = parent;      w->origX = origX; @@ -369,7 +371,7 @@ void wFlush(              void)  {      while (gtk_events_pending()) { -        gtk_main_iteration(); +        gtk_main_iteration_do(FALSE);      }      gdk_display_sync(gdk_display_get_default()); @@ -385,13 +387,81 @@ void wWinTop(wWin_p win)  }  /** - * Not implemented + * Set the cursor in GTK   *   * \param cursor IN   */ -void wSetCursor(wCursor_t cursor) +void wSetCursor(wDraw_p bd, wCursor_t cursor)  { +	static GdkCursor * gdkcursors[wCursorQuestion+1]; +	GdkCursor * gdkcursor; +	//GdkWindow * gdkwindow = gtk_widget_get_window(GTK_WIDGET(win->gtkwin));; +	GdkWindow * gdkwindow = gdk_get_default_root_window(); +	GdkDisplay * display = gdk_window_get_display(gdkwindow); +	if (!gdkcursors[cursor]) { +		switch(cursor) { +			case wCursorAppStart: +				//gdkcursor = gdk_cursor_new_from_name (display,"progress"); +				gdkcursor = gdk_cursor_new(GDK_WATCH); +				break; +			case wCursorHand: +				//gdkcursor = gdk_cursor_new_from_name (display,"pointer"); +				gdkcursor = gdk_cursor_new(GDK_HAND2); +							break; +			case wCursorNo: +				//gdkcursor = gdk_cursor_new_from_name (display,"not-allowed"); +				gdkcursor = gdk_cursor_new(GDK_X_CURSOR); +							break; +			case wCursorSizeAll: +				//gdkcursor = gdk_cursor_new_from_name (display,"move"); +				gdkcursor = gdk_cursor_new(GDK_FLEUR); +							break; +			case wCursorSizeNESW: +				//gdkcursor = gdk_cursor_new_from_name (display,"nesw-resize"); +				gdkcursor = gdk_cursor_new(GDK_BOTTOM_LEFT_CORNER); +							break; +			case wCursorSizeNS: +				//gdkcursor = gdk_cursor_new_from_name (display,"ns-resize"); +				gdkcursor = gdk_cursor_new(GDK_DOUBLE_ARROW); +							break; +			case wCursorSizeNWSE: +				//gdkcursor = gdk_cursor_new_from_name (display,"nwse-resize"); +				gdkcursor = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER); +							break; +			case wCursorSizeWE: +				//gdkcursor = gdk_cursor_new_from_name (display,"ew-resize"); +				gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW); +							break; +			case wCursorWait: +				//gdkcursor = gdk_cursor_new_from_name (display,"wait"); +				gdkcursor = gdk_cursor_new(GDK_WATCH); +							break; +			case wCursorIBeam: +				//gdkcursor = gdk_cursor_new_from_name (display,"text"); +				gdkcursor = gdk_cursor_new(GDK_XTERM); +							break; +			case wCursorCross: +				//gdkcursor = gdk_cursor_new_from_name (display,"crosshair"); +				gdkcursor = gdk_cursor_new(GDK_TCROSS); +							break; +			case wCursorQuestion: +				//gdkcursor = gdk_cursor_new_from_name (display,"help"); +				gdkcursor = gdk_cursor_new(GDK_QUESTION_ARROW); +							break; +			case wCursorNone: +				gdkcursor = gdk_cursor_new(GDK_BLANK_CURSOR); +			case wCursorNormal: +			default: +				//gdkcursor = gdk_cursor_new_from_name (display,"default"); +				gdkcursor = gdk_cursor_new(GDK_LEFT_PTR); +							break; + +		} +		gdkcursors[cursor] = gdkcursor; +	} else gdkcursor = gdkcursors[cursor]; + +	gdk_window_set_cursor ( gtk_widget_get_window(bd->widget), gdkcursor);  }  /** @@ -413,11 +483,17 @@ const char * wMemStats(void)  void wGetDisplaySize(wPos_t * w, wPos_t * h)  { - -    *w = gdk_screen_width(); -    *h = gdk_screen_height(); +	GdkScreen *screen = gdk_screen_get_default(); +	guint monitor = gdk_screen_get_primary_monitor(screen); +	GdkRectangle screen_geometry = { 0, 0, 0, 0 }; +	 +	gdk_screen_get_monitor_geometry( screen, monitor, &screen_geometry ); +	 +	*w = screen_geometry.width; +	*h = screen_geometry.height;  } +  static dynArr_t conversionBuffer_da;  /** diff --git a/app/wlib/gtklib/window.c b/app/wlib/gtklib/window.c index 49770c5..1468c89 100644 --- a/app/wlib/gtklib/window.c +++ b/app/wlib/gtklib/window.c @@ -29,6 +29,9 @@  #define GTK_DISABLE_DEPRECATED  #define GSEAL_ENABLE +#define MIN_WIDTH 100 +#define MIN_HEIGHT 100 +  #include <gtk/gtk.h>  #include <gdk/gdk.h>  #include <gdk/gdkkeysyms.h> @@ -37,8 +40,11 @@  wWin_p gtkMainW; -#define MIN_WIN_WIDTH (50) -#define MIN_WIN_HEIGHT (50) +#define MIN_WIN_WIDTH 150 +#define MIN_WIN_HEIGHT 150 + +#define MIN_WIN_WIDTH_MAIN 400 +#define MIN_WIN_HEIGHT_MAIN 400  #define SECTIONWINDOWSIZE  "gtklib window size"  #define SECTIONWINDOWPOS   "gtklib window pos" @@ -93,6 +99,7 @@ static GdkRectangle getMonitorDimensions(GtkWidget * widget) {  	gdk_screen_get_monitor_geometry(screen,monitor,&monitor_dimensions); +  	return monitor_dimensions;  } @@ -106,40 +113,47 @@ static GdkRectangle getMonitorDimensions(GtkWidget * widget) {  static void getWinSize(wWin_p win, const char * nameStr)  { -    int w, h; +    int w=50, h=50;      const char *cp;      char *cp1, *cp2; +      /*       * Clamp window to be no bigger than one monitor size (to start - the user can always maximize)       */      GdkRectangle monitor_dimensions = getMonitorDimensions(GTK_WIDGET(win->gtkwin)); -    wPos_t maxDisplayWidth = monitor_dimensions.width-5; -    wPos_t maxDisplayHeight = monitor_dimensions.height-25; +    wPos_t maxDisplayWidth = monitor_dimensions.width-10; +    wPos_t maxDisplayHeight = monitor_dimensions.height-50; -    if ((win->option&F_RESIZE) && + +    if ((win->option&F_RECALLSIZE) &&              (win->option&F_RECALLPOS) &&              (cp = wPrefGetString(SECTIONWINDOWSIZE, nameStr)) &&              (w = strtod(cp, &cp1), cp != cp1) &&              (h = strtod(cp1, &cp2), cp1 != cp2)) { -        if (w < 10) { -            w = 10; -        } - -        if (h < 10) { -            h = 10; -        } +    	win->option &= ~F_AUTOSIZE; -        if (w > maxDisplayWidth) w = maxDisplayWidth; -        if (h > maxDisplayHeight) h = maxDisplayHeight; +		if (w < 50) { +			w = 50; +		} -        win->w = win->origX = w; -        win->h = win->origY = h; -        win->option &= ~F_AUTOSIZE; +		if (h < 50) { +			h = 50; +		}      } + +	if (w > maxDisplayWidth) w = maxDisplayWidth; +	if (h > maxDisplayHeight) h = maxDisplayHeight; + +	if (w<MIN_WIDTH) w = MIN_WIDTH; +	if (h<MIN_HEIGHT) h = MIN_HEIGHT; + +	win->w = win->origX = w; +	win->h = win->origY = h; +  }  /** @@ -152,8 +166,7 @@ static void getWinSize(wWin_p win, const char * nameStr)  static void saveSize(wWin_p win)  { -    if ((win->option&F_RESIZE) && -            (win->option&F_RECALLPOS) && +    if ((win->option&F_RECALLSIZE) &&              gtk_widget_get_visible(GTK_WIDGET(win->gtkwin))) {          char pos_s[20]; @@ -210,7 +223,7 @@ static void getPos(wWin_p win)              }              gtk_window_move(GTK_WINDOW(win->gtkwin), x, y); -            gtk_window_resize(GTK_WINDOW(win->gtkwin), win->w, win->h); +            //gtk_window_resize(GTK_WINDOW(win->gtkwin), win->w, win->h);          }      }  } @@ -285,9 +298,17 @@ void wWinSetSize(  {      win->busy = TRUE;      win->w = width; -    win->h = height + BORDERSIZE + ((win->option&F_MENUBAR)?win->menu_height:0); -    gtk_widget_set_size_request(win->gtkwin, win->w, win->h); -    gtk_widget_set_size_request(win->widget, win->w, win->h); +   win->h = height + BORDERSIZE + ((win->option&F_MENUBAR)?MENUH:0); +    if (win->option&F_RESIZE) { +       	gtk_window_resize(GTK_WINDOW(win->gtkwin), win->w, win->h); +    	gtk_widget_set_size_request(win->widget, win->w-10, win->h-10); +    } +    else { +    	gtk_widget_set_size_request(win->gtkwin, win->w, win->h); +    	gtk_widget_set_size_request(win->widget, win->w, win->h); +    } + +      win->busy = FALSE;  } @@ -304,7 +325,7 @@ void wWinShow(      wWin_p win,		/* Window */      wBool_t show)		/* Command */  { -    GtkRequisition requisition; +    //GtkRequisition min_req, pref_req;      if (debugWindow >= 2) {          printf("Set Show %s\n", win->labelStr?win->labelStr:"No label"); @@ -314,31 +335,48 @@ void wWinShow(          abort();      } +    int width, height; +      if (show) {          keyState = 0;          getPos(win); +        if (!win->shown) { +			gtk_widget_show(win->gtkwin); +			gtk_widget_show(win->widget); +		} +          if (win->option & F_AUTOSIZE) { -            gtk_widget_size_request(win->gtkwin, &requisition); +        	GtkAllocation allocation; +        	GtkRequisition requistion; +        	gtk_widget_size_request(win->widget,&requistion); + +        	width = win->w; +        	height = win->h; + +            if (requistion.width != width || requistion.height != height ) { -            if (requisition.width != win->w || requisition.height != win->h) { -                //gtk_window_resize(GTK_WINDOW(win->gtkwin), win->w, win->h); -            	gtk_widget_set_size_request(win->gtkwin, win->w, win->h); -                gtk_widget_set_size_request(win->widget, win->w-20, win->h); +				width = requistion.width; +				height = requistion.height; + +            	win->w = width; +            	win->h = height; + + +            	gtk_window_set_resizable(GTK_WINDOW(win->gtkwin),TRUE);                  if (win->option&F_MENUBAR) {                      gtk_widget_set_size_request(win->menubar, win->w-20, MENUH); -                    GtkAllocation allocation; +                      gtk_widget_get_allocation(win->menubar, &allocation);                      win->menu_height = allocation.height;                  }              } +            gtk_window_resize(GTK_WINDOW(win->gtkwin), width+10, height+10);          } -        if (!win->shown) { -            gtk_widget_show(win->gtkwin); -            gtk_widget_show(win->widget); -        } +        gtk_window_present(GTK_WINDOW(win->gtkwin)); +          gdk_window_raise(gtk_widget_get_window(win->gtkwin)); @@ -606,11 +644,24 @@ static int fixed_expose_event(      GdkEventExpose * event,      wWin_p win)  { +	int rc; +      if (event->count==0) { -        return window_redraw(win, TRUE); +        rc = window_redraw(win, TRUE);      } else { -        return FALSE; +        rc = FALSE;      } +    cairo_t* cr = gdk_cairo_create (gtk_widget_get_window(widget)); +#ifdef CURSOR_SURFACE +    if (win && win->cursor_surface.surface && win->cursor_surface.show) { +		cairo_set_source_surface(cr,win->cursor_surface.surface,event->area.x, event->area.y); +		cairo_set_operator(cr,CAIRO_OPERATOR_OVER); +		cairo_rectangle(cr,event->area.x, event->area.y, +				event->area.width, event->area.height); +		cairo_fill(cr); +	} +#endif +    return rc;  }  static int resizeTime(wWin_p win) { @@ -738,10 +789,8 @@ wBool_t catch_shift_ctrl_alt_keys(      GdkEventKey *event,      void * data)  { -    int state; -    state = 0; - -    switch (event->keyval) { +    int state = 0; +    switch (event->keyval ) {      case GDK_KEY_Shift_L:      case GDK_KEY_Shift_R:          state |= WKEY_SHIFT; @@ -756,6 +805,13 @@ wBool_t catch_shift_ctrl_alt_keys(      case GDK_KEY_Alt_R:          state |= WKEY_ALT;          break; + +    case GDK_KEY_Meta_L: +    case GDK_KEY_Meta_R: +	// Pressing SHIFT and then ALT generates a Meta key +	//printf( "Meta\n" ); +        state |= WKEY_ALT; +        break;      }      if (state != 0) { @@ -764,10 +820,8 @@ wBool_t catch_shift_ctrl_alt_keys(          } else {              keyState &= ~state;          } -          return TRUE;      } -      return FALSE;  } @@ -786,7 +840,7 @@ static gint window_char_event(          return FALSE;      } -    if (event->state == 0) { +    if ( ( event->state & GDK_MODIFIER_MASK ) == 0 ) {          if (event->keyval == GDK_KEY_Escape) {              for (bb=win->first; bb; bb=bb->next) {                  if (bb->type == B_BUTTON && (bb->option&BB_CANCEL)) { @@ -804,6 +858,31 @@ static gint window_char_event(      }  } +void wSetGeometry(wWin_p win, int min_width, int max_width, int min_height, int max_height, int base_width, int base_height, double aspect_ratio ) { +	GdkGeometry hints; +	GdkWindowHints hintMask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; +    hints.min_width = min_width; +	hints.max_width = max_width; +	hints.min_height = min_height; +	hints.max_height = max_height; +	hints.min_aspect = hints.max_aspect = aspect_ratio; +	hints.base_width = base_width; +	hints.base_height = base_height; +	if( base_width != -1 && base_height != -1 ) { +		hintMask |= GDK_HINT_BASE_SIZE; +	} +	 +	if(aspect_ratio > -1.0 ) { +		hintMask |= GDK_HINT_ASPECT; +	}	 + +	gtk_window_set_geometry_hints( +			GTK_WINDOW(win->gtkwin), +			win->gtkwin, +			&hints, +			hintMask); +} +  /*   ******************************************************************************* @@ -862,12 +941,14 @@ static wWin_p wWinCommonCreate(          w->gtkwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);          if (gtkMainW) { -            gtk_window_set_transient_for(GTK_WINDOW(w->gtkwin), -                                         GTK_WINDOW(gtkMainW->gtkwin)); +        	if (!(w->option&F_NOTTRANSIENT)) +        		gtk_window_set_transient_for(GTK_WINDOW(w->gtkwin), +        									GTK_WINDOW(gtkMainW->gtkwin));          }      } +    getWinSize(w, nameStr);      if (winType != W_MAIN) { -            getWinSize(w, nameStr); +            gtk_widget_set_app_paintable (w->gtkwin,TRUE);      }      if (option & F_HIDE) { @@ -898,21 +979,39 @@ static wWin_p wWinCommonCreate(      gtk_container_add(GTK_CONTAINER(w->gtkwin), w->widget); + + +      if (w->option&F_AUTOSIZE) {          w->realX = 0; -        w->w = 0; +        w->w = MIN_WIN_WIDTH+20;          w->realY = h; -        w->h = 0; +        w->h = MIN_WIN_HEIGHT;      } else if (w->origX != 0){ -        w->w = w->realX = w->origX; -        w->h = w->realY = w->origY+h; -        gtk_window_set_default_size(GTK_WINDOW(w->gtkwin), w->w, w->h); +        w->realX = w->origX; +        w->realY = w->origY+h; + +        w->default_size_x = w->w; +        w->default_size_y = w->h;          //gtk_widget_set_size_request(w->widget, w->w-20, w->h);          if (w->option&F_MENUBAR) {              gtk_widget_set_size_request(w->menubar, w->w-20, MENUH);          }      } +    int scr_w, scr_h; +	wGetDisplaySize(&scr_w, &scr_h); +	if (scr_w < MIN_WIN_WIDTH) scr_w = MIN_WIN_WIDTH+10; +	if (scr_h < MIN_WIN_HEIGHT) scr_h = MIN_WIN_HEIGHT; +	if (winType != W_MAIN) { +		wSetGeometry(w, MIN_WIN_WIDTH, scr_w-10, MIN_WIN_HEIGHT, scr_h, -1, -1, -1); +	} else { +		if (scr_w < MIN_WIN_WIDTH_MAIN+10) scr_w = MIN_WIN_WIDTH_MAIN+200; +		if (scr_h < MIN_WIN_HEIGHT_MAIN+10) scr_h = MIN_WIN_HEIGHT_MAIN+200; +		wSetGeometry(w, MIN_WIN_WIDTH_MAIN, scr_w-10, MIN_WIN_HEIGHT_MAIN, scr_h-10, -1, -1, -1); +     } + +      w->first = w->last = NULL;      w->winProc = winProc; @@ -934,6 +1033,7 @@ static wWin_p wWinCommonCreate(      if (w->option & F_RESIZE) {          gtk_window_set_resizable(GTK_WINDOW(w->gtkwin), TRUE); +        gtk_window_resize(GTK_WINDOW(w->gtkwin), w->w, w->h);      } else {          gtk_window_set_resizable(GTK_WINDOW(w->gtkwin), FALSE);      } diff --git a/app/wlib/gtklib/wpref.c b/app/wlib/gtklib/wpref.c index c2541f9..124305a 100644 --- a/app/wlib/gtklib/wpref.c +++ b/app/wlib/gtklib/wpref.c @@ -168,7 +168,7 @@ const char * wGetAppWorkDir(  			if ( stat( appEtcConfig, &stFileInfo ) == 0 ) {  				char copyConfigCmd[(BUFSIZ * 2) + 3];  				sprintf( copyConfigCmd, "cp %s %s", appEtcConfig, appWorkDir ); -				system( copyConfigCmd ); +				int rc = system( copyConfigCmd );  			}  		}  	} @@ -293,7 +293,7 @@ void wPrefSetString(  			if (p->val)  				free(p->val);  			p->dirty = TRUE; -			p->val = strdup( sval ); +			p->val = (sval?strdup( sval ):NULL);  			return;  		}  	} @@ -302,7 +302,7 @@ void wPrefSetString(  	p->name = strdup(name);  	p->section = strdup(section);  	p->dirty = TRUE; -	p->val = strdup(sval); +	p->val = (sval?strdup(sval):NULL);  }  /** @@ -456,7 +456,9 @@ void wPrefFlush(  		return;  	for (p=&prefs(0); p<&prefs(prefs_da.cnt); p++) { -		fprintf( prefFile,  "%s.%s: %s\n", p->section, p->name, p->val ); +		if(p->val) { +			fprintf( prefFile,  "%s.%s: %s\n", p->section, p->name, p->val ); +		}	  	}  	fclose( prefFile );  } diff --git a/app/wlib/include/mswlib.h b/app/wlib/include/mswlib.h index 4a3f799..59260bb 100644 --- a/app/wlib/include/mswlib.h +++ b/app/wlib/include/mswlib.h @@ -1,8 +1,6 @@  #define WAPPICON	(980)  #define WM_F1DOWN	(WM_USER+10) -#define WM_NOTVALID	(WM_USER+11) -#define IDM_DOHELP	999  #define	IDM_PRINTAPP	998  #define	IDM_PRINTPAGE	997  #define IDM_ABOUT 100 diff --git a/app/wlib/include/wlib.h b/app/wlib/include/wlib.h index 699eefb..d3bfc18 100644 --- a/app/wlib/include/wlib.h +++ b/app/wlib/include/wlib.h @@ -11,6 +11,8 @@  #define FILE_SEP_CHAR "/"  #endif +#include <stdbool.h> +  #ifdef USE_SIMPLE_GETTEXT  char *bindtextdomain( char *domainname, char *dirname );  char *bind_textdomain_codeset(char *domainname, char *codeset ); @@ -20,6 +22,11 @@ char *gettext( const char *msgid );  char *g_win32_getlocale (void);  #endif +// conversion routines to and from UTF-8 +bool wSystemToUTF8(const char *inString, char *outString, unsigned outStringLength); +bool wUTF8ToSystem(const char *inString, char *outString, unsigned outStringLength); +bool wIsUTF8(const char * string); +  /*   * Interface types   */ @@ -148,12 +155,14 @@ wBool_t wNotice(		const char *, const char *, const char * );  int wNotice3(			const char *, const char *, const char *, const char * );  void wHelp(			const char * ); +  #define NT_INFORMATION 1  #define NT_WARNING	   2  #define NT_ERROR	   4  wBool_t wNoticeEx( int type, const char * msg, const char * yes, const char * no ); +unsigned wOpenFileExternal(char *filename);  void wSetBalloonHelp ( wBalloonHelp_t * ); @@ -170,11 +179,21 @@ unsigned long wGetTimer(	void );  void wExit(			int );  typedef enum {	wCursorNormal, +		wCursorNone, +		wCursorAppStart, +		wCursorHand, +		wCursorNo, +		wCursorSizeAll, +		wCursorSizeNESW, +		wCursorSizeNS, +		wCursorSizeNWSE, +		wCursorSizeWE,  		wCursorWait,  		wCursorIBeam,  		wCursorCross,  		wCursorQuestion } wCursor_t; -void wSetCursor( wCursor_t ); +void wSetCursor( wDraw_p, wCursor_t ); +#define defaultCursor wCursorCross  const char * wMemStats( void ); @@ -224,6 +243,8 @@ typedef void (*wWinCallBack_p)( wWin_p, winProcEvent, void *, void * );  #define F_CENTER	(1L<<12)  #define F_HIDE		(1L<<13)  #define F_MAXIMIZE  (1L<<14) +#define F_RESTRICT  (1L<<15) +#define F_NOTTRANSIENT (1L<<16)  wWin_p wWinMainCreate(	        const char *, wPos_t, wPos_t, const char *, const char *, const char *,  				long, wWinCallBack_p, void * ); @@ -246,6 +267,7 @@ void wMessage(			wWin_p, const char *, wBool_t );  void wWinTop(			wWin_p );  void wWinDoCancel(		wWin_p );  void wWinBlockEnable(		wBool_t ); +void wSetGeometry(wWin_p, int min_width, int max_width, int min_height, int max_height, int base_width, int base_height, double aspect_ratio);  int wCreateSplash( char *appName, char *appVer );  int wSetSplashInfo( char *msg ); @@ -262,6 +284,7 @@ void wDestroySplash( void );  #define BO_READONLY	(1L<<2)  #define BO_NOTAB	(1L<<8)  #define BO_BORDER	(1L<<9) +#define BO_ENTER    (1L<<10)  wPos_t wLabelWidth(		const char * );  const char * wControlGetHelp(		wControl_p ); @@ -290,7 +313,7 @@ void wControlLinkedActive( wControl_p b, int active );  #define BS_TRIM			(1<<12)  /* Creation CallBacks */ -typedef void (*wStringCallBack_p)( const char *, void * ); +typedef void (*wStringCallBack_p)( const char *, void *);  wString_p wStringCreate(	wWin_p, wPos_t, wPos_t, const char *, const char *, long,  				wPos_t, char *, wIndex_t, wStringCallBack_p,  				void * ); @@ -305,8 +328,8 @@ const char * wStringGetValue(		wString_p );   */  /* Creation CallBacks */ -typedef void (*wIntegerCallBack_p)( long, void * ); -typedef void (*wFloatCallBack_p)( double, void * ); +typedef void (*wIntegerCallBack_p)( long, void * , int); +typedef void (*wFloatCallBack_p)( double, void * , int);  wInteger_p wIntegerCreate(	wWin_p, wPos_t, wPos_t, const char *, const char *, long,  				wPos_t, wInteger_t, wInteger_t, wInteger_t *,  				wIntegerCallBack_p, void * ); @@ -414,6 +437,7 @@ wLine_p wLineCreate(		wWin_p, const char *, int, wLines_t *);  #define BT_CHARUNITS	(1L<<23)  #define BT_FIXEDFONT	(1L<<22)  #define BT_DOBOLD	(1L<<21) +#define BT_TOP		(1L<<20)	/* Show the top of the text */  wText_p wTextCreate(		wWin_p, wPos_t, wPos_t, const char *, const char *, long,  				wPos_t, wPos_t ); @@ -440,12 +464,34 @@ void wTextSetPosition(		wText_p bt, int pos );  typedef int wDrawOpts;  #define wDrawOptTemp	(1<<0)  #define wDrawOptNoClip	(1<<1) +#define wDrawOptTransparent  (1<<2) +#define wDrawOutlineFont (1<<3) +#ifdef CURSOR_SURFACE +#define wDrawOptCursor  (1<<4) +#define wDrawOptCursorClr (1<<5) +#define wDrawOptCursorClr (1<<6) +#define wDrawOptCursorRmv (1<<7) +#define wDrawOptCursorQuit (1<<8) +#define wDrawOptOpaque   (1<<9) +#endif +  typedef enum {  	wDrawLineSolid, -	wDrawLineDash } +	wDrawLineDash, +	wDrawLineDot, +	wDrawLineDashDot, +	wDrawLineDashDotDot, +	wDrawLineCenter, +	wDrawLinePhantom}  		wDrawLineType_e; +typedef enum { +	wPolyLineStraight, +	wPolyLineSmooth, +	wPolyLineRound} +	wPolyLine_e; +  typedef int wAction_t;  #define wActionMove		(1)  #define wActionLDown		(2) @@ -458,7 +504,13 @@ typedef int wAction_t;  #define wActionExtKey		(9)  #define wActionWheelUp (10)  #define wActionWheelDown (11) -#define wActionLast		wActionWheelDown +#define wActionLDownDouble (12) +#define wActionModKey (13) +#define wActionScrollUp (14) +#define wActionScrollDown (15) +#define wActionScrollLeft (16) +#define wActionScrollRight (17) +#define wActionLast		wActionScrollRight  #define wRGB(R,G,B)\ @@ -474,6 +526,7 @@ typedef void (*wDrawActionCallBack_p)(	wDraw_p, void*, wAction_t, wPos_t, wPos_t  #define BD_DIRECT	(1L<<26)  #define BD_NOCAPTURE (1L<<27)  #define BD_NOFOCUS  (1L<<28) +#define BD_MODKEYS  (1L<<29)  /* Create: */  wDraw_p wDrawCreate(		wWin_p, wPos_t, wPos_t, const char *, long, @@ -496,13 +549,15 @@ void wDrawString(		wDraw_p, wPos_t, wPos_t, wAngle_t, const char *, wFont_p,  		  		wFontSize_t, wDrawColor, wDrawOpts );  void wDrawFilledRectangle(	wDraw_p, wPos_t, wPos_t, wPos_t, wPos_t,  				wDrawColor, wDrawOpts ); -void wDrawFilledPolygon(	wDraw_p, wPos_t [][2], wIndex_t, wDrawColor, -				wDrawOpts ); +void wDrawPolygon(	wDraw_p, wPos_t [][2], wPolyLine_e [], wIndex_t, wDrawColor, wDrawWidth, wDrawLineType_e, +				wDrawOpts, int, int );  void wDrawFilledCircle(		wDraw_p, wPos_t, wPos_t, wPos_t, wDrawColor, wDrawOpts ); -void wDrawGetTextSize(		wPos_t *, wPos_t *, wPos_t *, wDraw_p, const char *, wFont_p, +void wDrawGetTextSize(		wPos_t *, wPos_t *, wPos_t *, wPos_t *, wDraw_p, const char *, wFont_p,  				wFontSize_t );  void wDrawClear(		wDraw_p ); +void wDrawClearTemp(		wDraw_p ); +wBool_t wDrawSetTempMode(	wDraw_p, wBool_t );  void wDrawDelayUpdate(		wDraw_p, wBool_t );  void wDrawClip(			wDraw_p, wPos_t, wPos_t, wPos_t, wPos_t ); @@ -517,7 +572,7 @@ void wDrawSetSize(		wDraw_p, wPos_t, wPos_t, void * );  void wDrawGetSize(		wDraw_p, wPos_t *, wPos_t * );  /* Bitmaps */ -wDrawBitMap_p wDrawBitMapCreate( wDraw_p, int, int, int, int, const char * ); +wDrawBitMap_p wDrawBitMapCreate( wDraw_p, int, int, int, int, const unsigned char * );  void wDrawBitMap(		wDraw_p, wDrawBitMap_p, wPos_t, wPos_t,  				wDrawColor, wDrawOpts ); @@ -529,6 +584,8 @@ wBool_t wBitMapWriteFile(	wDraw_p, const char * );  void * wDrawGetContext(		wDraw_p );  void wDrawSaveImage(		wDraw_p );  void wDrawRestoreImage(		wDraw_p ); +int wDrawSetBackground(    wDraw_p, char * path, char ** error); +void wDrawShowBackground(   wDraw_p, wPos_t pos_x, wPos_t pos_y, wPos_t width, wAngle_t angle, int screen);  /*------------------------------------------------------------------------------   * @@ -537,7 +594,7 @@ void wDrawRestoreImage(		wDraw_p );  void wInitializeFonts();  void wSelectFont(		const char * );  wFontSize_t wSelectedFontSize(	void ); -void wSetSelectionFontSize(int); +void wSetSelectionFontSize(wFontSize_t);  #define F_TIMES	(1)  #define F_HELV	(2)  wFont_p wStandardFont(		int, wBool_t, wBool_t ); @@ -548,22 +605,19 @@ wFont_p wStandardFont(		int, wBool_t, wBool_t );   * Printing   */ -typedef void (*wAddPrinterCallBack_p)( const char *, const char * ); -typedef void (*wAddMarginCallBack_p)( const char *, double, double, double, double ); -typedef void (*wAddFontAliasCallBack_p)( const char *, const char * );  typedef void (*wPrintSetupCallBack_p)( wBool_t );  wBool_t wPrintInit(		void );  void wPrintSetup(		wPrintSetupCallBack_p ); -void wPrintSetCallBacks(	wAddPrinterCallBack_p, wAddMarginCallBack_p, wAddFontAliasCallBack_p ); +void wPrintGetMargins(		double *, double *, double *, double * );  void wPrintGetPageSize(		double *, double * ); -void wPrintGetPhysSize(		double *, double * );  wBool_t wPrintDocStart(		const char *, int, int * );  wDraw_p wPrintPageStart(	void );  wBool_t wPrintPageEnd(		wDraw_p );  void wPrintDocEnd(		void );  wBool_t wPrintQuit(		void );  void wPrintClip(		wPos_t, wPos_t, wPos_t, wPos_t ); +const char * wPrintGetName(	void );  /*------------------------------------------------------------------------------ @@ -607,6 +661,15 @@ typedef enum {  	wAccelKey_LineFeed }  	wAccelKey_e; +typedef enum { +	wModKey_None, +	wModKey_Alt, +	wModKey_Shift, +	wModKey_Ctrl } +	wModKey_e; + +void wDoAccelHelp( wAccelKey_e key, void * ); +  /* Creation CallBacks */  typedef void (*wMenuCallBack_p)( void * );  typedef void (*wMenuListCallBack_p)( int, const char *, void * ); @@ -656,6 +719,7 @@ void wAttachAccelKey( wAccelKey_e, int, wAccelKeyCallBack_p, void * );   */  #define FS_MULTIPLEFILES	1 +#define FS_PICTURES         2  struct wFilSel_t;  typedef enum { @@ -726,4 +790,11 @@ wPos_t wStatusGetHeight(long flags);  void wStatusSetValue(wStatus_p b, const char * arg);  void wStatusSetWidth(wStatus_p b, wPos_t width); + +/*------------------------------------------------------------------------------- + * User Preferences + */ + +#define PREFSECTION "Preference" +#define LARGEICON   "LargeIcons"  #endif diff --git a/app/wlib/mswlib/CMakeLists.txt b/app/wlib/mswlib/CMakeLists.txt index 82d8371..07558f9 100644 --- a/app/wlib/mswlib/CMakeLists.txt +++ b/app/wlib/mswlib/CMakeLists.txt @@ -1,12 +1,13 @@ +find_package(FreeImage REQUIRED) +  FILE(GLOB HEADERS *.h)  SET(SOURCES -#	checksum.c +	backgnd.c  	getopt.c  	mswbox.c  	mswbutt.c  	mswbitmap.c -	mswchksm.c  	mswchoic.c  	mswcolor.c  	mswdraw.c @@ -19,14 +20,15 @@ SET(SOURCES  	mswpref.c  	mswprint.c  	mswsplash.c -	mswstatus.c  +	mswstatus.c  	mswtext.c  	gwin32.c  	simple-gettext.c +	utf8conv.c  	) +include_directories(${FREEIMAGE_INCLUDE_PATH})  INCLUDE_DIRECTORIES(${XTrkCAD_BINARY_DIR}) -# INCLUDE_DIRECTORIES(${XTRKCAD_BINARY_DIR})  IF(XTRKCAD_USE_GETTEXT)  	IF(WIN32) @@ -37,4 +39,13 @@ ENDIF(XTRKCAD_USE_GETTEXT)  ADD_LIBRARY(xtrkcad-wlib ${HEADERS} ${SOURCES})  TARGET_LINK_LIBRARIES(xtrkcad-wlib Htmlhelp msimg32 shlwapi) +target_link_libraries(xtrkcad-wlib ${FREEIMAGE_LIBRARY}) + +install(FILES +	${FREEIMAGE_SHAREDLIB} +	DESTINATION ${XTRKCAD_BIN_INSTALL_DIR} +	) +if(XTRKCAD_TESTING AND CMOCKA_FOUND) +	add_subdirectory( unittest ) +endif() diff --git a/app/wlib/mswlib/backgnd.c b/app/wlib/mswlib/backgnd.c new file mode 100644 index 0000000..d35f19a --- /dev/null +++ b/app/wlib/mswlib/backgnd.c @@ -0,0 +1,220 @@ +/** \file backgnd.c +* Layout background image +*/ + +/*  XTrkCad - Model Railroad CAD +*  Copyright (C) 2018 Martin Fischer +* +*  This program is free software; you can redistribute it and/or modify +*  it under the terms of the GNU General Public License as published by +*  the Free Software Foundation; either version 2 of the License, or +*  (at your option) any later version. +* +*  This program is distributed in the hope that it will be useful, +*  but WITHOUT ANY WARRANTY; without even the implied warranty of +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +*  GNU General Public License for more details. +* +*  You should have received a copy of the GNU General Public License +*  along with this program; if not, write to the Free Software +*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include <windows.h> + +#include <FreeImage.h> +#include "i18n.h" +#include "mswint.h" + +static char *lastErrorMessage;		/**< store last message from FreeImage */ +#define ERRORPUNCTUATION " : " + +/**
 + * FreeImage error handler
 + * \param fif Format / Plugin responsible for the error
 + * \param message Error message
 + */ + +static void 
 +HandleFreeImageError(FREE_IMAGE_FORMAT fif, const char *message) 
 +{
 +	unsigned totalLength = strlen(message) + 1;
 +
 +	if (fif != FIF_UNKNOWN) {
 +		totalLength += strlen(FreeImage_GetFormatFromFIF(fif)) + strlen(ERRORPUNCTUATION);
 +	}
 +
 +	lastErrorMessage = malloc(totalLength);
 +
 +	if (fif != FIF_UNKNOWN) {
 +		sprintf(lastErrorMessage,
 +				"%s" ERRORPUNCTUATION "%s",
 +				FreeImage_GetFormatFromFIF(fif),
 +				message);
 +	} else {
 +		strcpy(lastErrorMessage, message);
 +	}
 +} + +/**
 +* Load the background image
 +* \param bd drawing context
 +* \param path filename for image file, if NULL the existing background will be removed
 +* \param error returned error message
 +* \return -1 unsupported or invalid file, 0 success, 1 background removed
 +*/ + +int +wDrawSetBackground(wDraw_p bd, char * path, char ** error) +{ +    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; + +	FreeImage_SetOutputMessage(HandleFreeImageError); +
 +	if (lastErrorMessage) {
 +		free(lastErrorMessage);
 +		lastErrorMessage = NULL;
 +	} + +    if (path) { +        // check the file signature and deduce its format +        // (the second argument is currently not used by FreeImage) +        fif = FreeImage_GetFileType(path, 0); + +        if (fif == FIF_UNKNOWN) { +            // no signature ? +            // try to guess the file format from the file extension +            fif = FreeImage_GetFIFFromFilename(path); +        } + +        // check that the plugin has reading capabilities ... +        if ((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) { +            // ok, let's load the file +            bd->background = FreeImage_Load(fif, path, 0); + +            // unless a bad file format, we are done ! +            if (!bd->background) { +                *error = lastErrorMessage; +                return (-1); +            } else { +                return (0); +            } +        } else { +			*error = strdup(_("Image file is invalid or cannot be read.")); +            return (-1); +        } +    } else { +        if (bd->background) { +            FreeImage_Unload(bd->background); +            bd->background = 0; +        } + +        return (1); +    } +} + +/**
 +* Draw background to screen. The background will be sized and rotated before being shown. The bitmap 
 +* is scaled so that the width is equal to size. The height is changed proportionally. 
 +*
 +* \param bd drawing context
 +* \param pos_x, pos_y bitmap position
 +* \param size desired width after scaling
 +* \param angle 
 +* \param screen visibility of bitmap in percent
 +*/ + +void +wDrawShowBackground(wDraw_p bd, wPos_t pos_x, wPos_t pos_y, wPos_t size, +                    wAngle_t angle, int screen) +{ +    if (bd->background) { +        double scale; +        FIBITMAP *tmp; +        FIBITMAP *rotated; + +        if (size == 0) { +            scale = 1.0; +        } else { +            scale = (double)size / FreeImage_GetWidth(bd->background); +        } + +        tmp = FreeImage_RescaleRect(bd->background, +                                    (int)((double)FreeImage_GetWidth(bd->background) * scale), +                                    (int)((double)FreeImage_GetHeight(bd->background) * scale), +                                    0, +                                    0, +                                    FreeImage_GetWidth(bd->background), +                                    FreeImage_GetHeight(bd->background), +                                    FILTER_BILINEAR, +                                    0); +        FreeImage_AdjustColors(tmp, screen, -screen, 1.0, FALSE); +        FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(tmp); + +        switch (image_type) { +        case FIT_BITMAP: +            switch (FreeImage_GetBPP(tmp)) { +            case 8: { +                BYTE color = 255; +                rotated = FreeImage_Rotate(tmp, angle, &color); +            } +            break; + +            case 24: // we could also use 'RGBTRIPLE color' here +            case 32: { +                RGBQUAD color = { 255, 255, 255, 0 }; +                // for 24-bit images, the first 3 bytes will be read +                // for 32-bit images, the first 4 bytes will be read +                rotated = FreeImage_Rotate(tmp, angle, &color); +            } +            break; +            } + +            break; + +        case FIT_UINT16: { +            WORD color = 255; +            rotated = FreeImage_Rotate(tmp, angle, &color); +        } +        break; + +        case FIT_RGB16: // we could also use 'FIRGB16 color' here +        case FIT_RGBA16: { +            FIRGBA16 color = { 255, 255, 255, 0 }; +            // for RGB16 images, the first 3 WORD will be read +            // for RGBA16 images, the first 4 WORD will be read +            rotated = FreeImage_Rotate(tmp, angle, &color); +        } +        break; + +        case FIT_FLOAT: { +            float color = 1.0F; +            rotated = FreeImage_Rotate(tmp, angle, &color); +        } +        break; + +        case FIT_RGBF: // we could also use 'FIRGBF color' here +        case FIT_RGBAF: { +            FIRGBAF color = { 1, 1, 1, 0 }; +            // for RGBF images, the first 3 float will be read +            // for RGBAF images, the first 4 float will be read +            rotated = FreeImage_Rotate(tmp, angle, &color); +        } +        break; +        } + +        SetDIBitsToDevice(bd->hDc, +                          pos_x, +                          bd->h - pos_y - FreeImage_GetHeight(rotated), +                          FreeImage_GetWidth(rotated), +                          FreeImage_GetHeight(rotated), +                          0, 0, +                          0, +                          FreeImage_GetHeight(rotated), +                          FreeImage_GetBits(rotated), +                          FreeImage_GetInfo(rotated), +                          DIB_RGB_COLORS); +        FreeImage_Unload(tmp); +        FreeImage_Unload(rotated); +    } +}
\ No newline at end of file diff --git a/app/wlib/mswlib/mswbitmap.c b/app/wlib/mswlib/mswbitmap.c index e369e78..95b8a69 100644 --- a/app/wlib/mswlib/mswbitmap.c +++ b/app/wlib/mswlib/mswbitmap.c @@ -24,10 +24,12 @@  #include <windows.h>  #include <string.h>  #include <malloc.h> +#include <math.h>  #include <stdlib.h>  #include <commdlg.h>  #include <stdio.h>  #include <assert.h> +#include "misc.h"  #include "mswint.h"  #include "i18n.h" @@ -177,14 +179,15 @@ void mswDrawIcon(  		memset( bmiInfo->bmiColors, 0, bm->colorcnt * sizeof( RGBQUAD ));  		memset( &bmiInfo->bmiColors[ bm->transparent ], 0xFF, sizeof( RGBQUAD ));  	} +  	StretchDIBits(hDc, offw, offh, -        bmiInfo->bmiHeader.biWidth, -        bmiInfo->bmiHeader.biHeight, -        0, 0, -        bmiInfo->bmiHeader.biWidth, -        bmiInfo->bmiHeader.biHeight, -        bm->pixels, bmiInfo, 				 -        DIB_RGB_COLORS, SRCAND); +				 (int)ceil(bmiInfo->bmiHeader.biWidth*scaleIcon), +	             (int)ceil(bmiInfo->bmiHeader.biHeight*scaleIcon), +	              0, 0, +	              bmiInfo->bmiHeader.biWidth, +	              bmiInfo->bmiHeader.biHeight, +	              bm->pixels, bmiInfo, +	              DIB_RGB_COLORS, SRCAND);  	/* now paint the bitmap with transparent set to black */  	if( bm->type == mswIcon_bitmap ) { @@ -221,16 +224,16 @@ void mswDrawIcon(          }  		memset( &bmiInfo->bmiColors[ bm->transparent ], 0, sizeof( RGBQUAD ));      } -     +      /* show the bitmap */      StretchDIBits(hDc, offw, offh, -            bmiInfo->bmiHeader.biWidth, -            bmiInfo->bmiHeader.biHeight, -            0, 0, -            bmiInfo->bmiHeader.biWidth, -            bmiInfo->bmiHeader.biHeight, -            bm->pixels, bmiInfo, 				 -            DIB_RGB_COLORS, SRCPAINT); +				 (int)ceil(bmiInfo->bmiHeader.biWidth*scaleIcon), +	             (int)ceil(bmiInfo->bmiHeader.biHeight*scaleIcon), +                  0, 0, +                  bmiInfo->bmiHeader.biWidth, +                  bmiInfo->bmiHeader.biHeight, +                  bm->pixels, bmiInfo, +                  DIB_RGB_COLORS, SRCPAINT);      /* forget the data */      free( bmiInfo ); @@ -434,11 +437,14 @@ wIcon_p wIconCreatePixMap( char *pm[])  			/* look up pixel info in color table */  			k = 0; -			while( pixel != keys[ k ] ) +			while(k < col && pixel != keys[ k ] )  				k++; - -			/* save the index into color table */ -			*(cq + j) = k; +			if (pixel == keys[k]) { +				/* save the index into color table */ +				*(cq + j) = k; +			} else { +				*(cq + j) = 0; +			}  		}  	}		  	free( keys ); @@ -507,4 +513,4 @@ wBitmapCreate( wWin_p parent, wPos_t x, wPos_t y, long option, wIcon_p iconP )  	control->data = iconP;  	return (wControl_p)control; -}
\ No newline at end of file +} diff --git a/app/wlib/mswlib/mswbutt.c b/app/wlib/mswlib/mswbutt.c index d213695..16f31c1 100644 --- a/app/wlib/mswlib/mswbutt.c +++ b/app/wlib/mswlib/mswbutt.c @@ -37,11 +37,7 @@ int kludge12 = 0;   *****************************************************************************   */ - -  static XWNDPROC oldButtProc = NULL; -static XWNDPROC newButtProc; -  struct wButton_t {  		WOBJ_COMMON @@ -88,9 +84,9 @@ static void drawButton(  	COLORREF colF;  #define LEFT (0) -#define RIGHT (bm->w+10) +#define RIGHT (LONG)ceil(bm->w*scaleIcon+10)  #define TOP (0) -#define BOTTOM (bm->h+10) +#define BOTTOM (LONG)ceil(bm->h*scaleIcon+10)  	/* get the lightest and the darkest color to use */  	colL = GetSysColor( COLOR_BTNHIGHLIGHT ); @@ -239,6 +235,7 @@ static LRESULT buttPush( wControl_p b, HWND hWnd, UINT message, WPARAM wParam, L  	DRAWITEMSTRUCT * di = (DRAWITEMSTRUCT *)lParam;  	wBool_t selected; +  	switch (message) {  	case WM_COMMAND:  		if (bb->action /*&& !bb->busy*/) { @@ -253,8 +250,8 @@ static LRESULT buttPush( wControl_p b, HWND hWnd, UINT message, WPARAM wParam, L  			break;  		mi->CtlType = ODT_BUTTON;  		mi->CtlID = wParam; -		mi->itemWidth = bb->w; -		mi->itemHeight = bb->h; +		mi->itemWidth = (UINT)ceil(bb->w*scaleIcon); +		mi->itemHeight = (UINT)ceil(bb->h*scaleIcon);  		} return 0L;  	case WM_DRAWITEM: @@ -369,8 +366,8 @@ wButton_p wButtonCreate(  	b->selected = 0;  	mswComputePos( (wControl_p)b, x, y );  	if (b->option&BO_ICON) { -		width = bm->w+10; -		h = bm->h+10; +		width = (wPos_t)ceil(bm->w*scaleIcon)+10; +		h = (int)ceil(bm->h*scaleIcon)+10;  		b->icon = bm;  	} else {  		width = (wPos_t)(width*mswScale); @@ -405,5 +402,9 @@ wButton_p wButtonCreate(  	}  	if ( !mswThickFont )  		SendMessage( b->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, 0L ); + + +	InvalidateRect(b->hWnd, &rect, TRUE); +  	return b;  } diff --git a/app/wlib/mswlib/mswchksm.c b/app/wlib/mswlib/mswchksm.c deleted file mode 100644 index 602c204..0000000 --- a/app/wlib/mswlib/mswchksm.c +++ /dev/null @@ -1,125 +0,0 @@ -#include <stdio.h> -#include <sys/stat.h> -#include "../include/wlib.h" -#ifdef WINDOWS -#include <windows.h> -#include "mswint.h" -#endif - -#define HEWHDROFFSET	(0x3C) - -static FILE * openfile( const char * fn, const char * mode, long * fileSize ) -{ -	unsigned short PageCnt; -	long FileSize; -	FILE *fp; -	struct stat Stat; -	fp = fopen( fn, mode ); -	if (fp == NULL) { -		perror( "fopen" ); -		return NULL; -	} -	fread( &PageCnt, sizeof(PageCnt), 1, fp ); /* Read past signature */ -	fread( &PageCnt, sizeof(PageCnt), 1, fp ); /* Read past pagesize */ -	FileSize = PageCnt; -	fread( &PageCnt, sizeof(PageCnt), 1, fp ); /* Read past pagesize */ -	if ( FileSize == 0L ) -		FileSize = PageCnt * 512L; -	else -		FileSize += (PageCnt - 1) * 512L; -	*fileSize = FileSize; -	stat( fn, &Stat ); -	*fileSize = (long)Stat.st_size; -	fprintf( stderr, "size1 = %ld, size2 = %ld\n", FileSize, (long)Stat.st_size ); -	return fp; -} - - -static unsigned short mswCheck16( FILE * fp, long FileSize, unsigned short * sum16stored ) -{ -	unsigned short int sum16, NxtInt; -	long x; -	unsigned char NxtChar; -	sum16 = 0; -	fseek(fp, 0, SEEK_SET); - -	for (x=0L; x<FileSize/2L; x++) { -		fread( &NxtInt, sizeof NxtInt, 1, fp ); -		if (x == 9) -			*sum16stored = NxtInt; -		else -			sum16 += NxtInt; -	} -	if (FileSize%2) { -		fread( &NxtChar, sizeof NxtChar, 1, fp ); -		sum16 += (unsigned int)NxtChar; -	} -	return sum16; -} - - -static int mswCheck32( FILE * fp, long FileSize, long * sum32off, unsigned long * sum32computed, unsigned long * sum32stored ) -{ -	unsigned long sum32, NxtLong; -	long x; -	long NewHdrOffset; -	unsigned char NxtByte, y; - -	fseek( fp, HEWHDROFFSET, SEEK_SET ); -	fread( &NewHdrOffset, sizeof NewHdrOffset, 1, fp ); -	if (NewHdrOffset == 0) { -		fprintf( stderr, "NewHdrOffset == 0\n" ); -		return 0; -	} -	NewHdrOffset = (NewHdrOffset/4)*4; -	*sum32off = NewHdrOffset + 8; -	sum32 = 0L; -	fseek( fp, 0, SEEK_SET ); -	for (x = ( NewHdrOffset + 8 ) / 4; x; x-- ) { -		fread( &NxtLong, sizeof NxtLong, 1, fp ); -		sum32 += NxtLong; -	} -	fread( sum32stored, sizeof sum32stored, 1, fp ); - -	for (x=0; x<(FileSize-NewHdrOffset - 12)/4; x++) { -		fread( &NxtLong, sizeof NxtLong, 1, fp ); -		sum32 += NxtLong; -	} -	if ( 0L != (x=FileSize%4L) ) { -		NxtLong = 0L; -		for (y=0; y<x; y++ ) { -			fread( &NxtByte, sizeof NxtByte, 1, fp ); -			NxtLong += (unsigned long)NxtByte << (8*y); -		} -		sum32 += NxtLong; -	} -	*sum32computed = sum32; -	return 1; -} - - -#ifdef WINDOWS -wBool_t wCheckExecutable( void ) -{ -	char fileName[1024]; -	FILE * fp; -	long FileSize; -	GetModuleFileName( mswHInst, fileName, sizeof fileName ); -	fp = openfile( fileName, "rb", &FileSize ); -#ifdef LATER -	{ -		unsigned long int sum32offset, sum32computed, sum32stored; -		if ( ! mswCheck32( fp, FileSize, &sum32offset, &sum32computed, &sum32stored ) ) -			return FALSE; -		return sum32computed == sum32stored; -	} -#else -	{ -		unsigned short int sum16computed, sum16stored; -		sum16computed = mswCheck16( fp, FileSize, &sum16stored ); -		sum16computed += sum16stored; -		return sum16computed == 0xFFFF; -	} -#endif -} -#endif diff --git a/app/wlib/mswlib/mswdraw.c b/app/wlib/mswlib/mswdraw.c index bf0ab76..c2739e6 100644 --- a/app/wlib/mswlib/mswdraw.c +++ b/app/wlib/mswlib/mswdraw.c @@ -1,8 +1,26 @@ -/* - * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/wlib/mswlib/mswdraw.c,v 1.6 2009-05-15 18:16:16 m_fischer Exp $ +/** \file mswdraw.c + * Draw basic geometric shapes   */ -#define _WIN32_WINNT 0x0500		/* for wheel mouse supposrt */ +/*  XTrackCAD - Model Railroad CAD + *  Copyright (C) 2005 Dave Bullis + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */	 + +#define _WIN32_WINNT 0x0600		/* for wheel mouse supposrt */  #include <windows.h>  #include <string.h>  #include <malloc.h> @@ -16,8 +34,12 @@  #else  #define wFont_t tagLOGFONT  #endif + +#include "misc.h"  #include "mswint.h" +#include <FreeImage.h> +wBool_t wDrawDoTempDraw = TRUE;  /*   *****************************************************************************   * @@ -39,6 +61,8 @@ static long clrOp = 0xbb0226;  #define CENTERMARK_LENGTH 6 +bool bDrawMainBM = 0; +  #ifdef SLOW  static wPos_t XPIX2INCH( wDraw_p d, int ix )  { @@ -119,6 +143,31 @@ void wDrawDelayUpdate(  {  } +wBool_t wDrawSetTempMode( +	wDraw_p bd, +	wBool_t bTemp ) +{ +	wBool_t rc = bd->bTempMode; +	bd->bTempMode = bTemp; +	if (rc == FALSE && bTemp == TRUE) { +		// Main to Temp drawing +		// Copy mainBM to tempBM +		wDrawClearTemp( bd ); +		if (bDrawMainBM) return rc; +		HDC hDcOld = CreateCompatibleDC(bd->hDc); +		HBITMAP hBmOld = SelectObject(hDcOld, bd->hBmMain); +		SelectObject(bd->hDc, bd->hBmTemp); +		BitBlt(bd->hDc, 0, 0, +			bd->w, bd->h, +			hDcOld, 0, 0, +			SRCCOPY); +		SelectObject(hDcOld, hBmOld); +		DeleteDC(hDcOld); +		bd->bCopiedMain = TRUE; +	} +	return rc; +} +  /**   * Sets the proper pen and composition for the next drawing operation   *  @@ -130,68 +179,83 @@ void wDrawDelayUpdate(   * \param dc IN color   * \param dopt IN ????   */ -  static void setDrawMode( -		HDC hDc,  		wDraw_p d,  		wDrawWidth dw,  		wDrawLineType_e lt,  		wDrawColor dc,  		wDrawOpts dopt )  { -	int mode; +	long centerPen[] = {40,10,20,10}; +	long phantomPen[] = {40,10,20,10,20,10}; +  	HPEN hOldPen;  	static wDraw_p d0;  	static wDrawWidth dw0 = -1;  	static wDrawLineType_e lt0 = (wDrawLineType_e)-1;  	static wDrawColor dc0 = -1; -	static int mode0 = -1;  	static LOGBRUSH logBrush = { 0, 0, 0 };  	DWORD penStyle; +	if ( wDrawDoTempDraw && (dopt & wDrawOptTemp) ) +		SelectObject(d->hDc, d->hBmTemp); +	else +		SelectObject(d->hDc, d->hBmMain); +  	if ( d->hasPalette ) {  		int winPaletteClock = mswGetPaletteClock();  		if ( d->paletteClock < winPaletteClock ) { -			RealizePalette( hDc ); +			RealizePalette( d->hDc );  			d->paletteClock = winPaletteClock;  		}  	} -	if (dopt & wDrawOptTemp) { -		mode = R2_NOTXORPEN; -	} else { -		mode = R2_COPYPEN; -	} -	SetROP2( hDc, mode ); -	if ( d == d0 && mode == mode0 && dw0 == dw && lt == lt0 && dc == dc0 ) +	SetROP2( d->hDc, R2_COPYPEN ); +	if ( d == d0 && dw0 == dw && lt == lt0 && dc == dc0 )  		return;  	// make sure that the line width is at least 1!  	if( !dw )   		dw++; -	d0 = d; mode0 = mode; dw0 = dw; lt0 = lt; dc0 = dc; +	d0 = d; dw0 = dw; lt0 = lt; dc0 = dc; + +	void * penarray = NULL; +	int penarray_size = 0;  	logBrush.lbColor = mswGetColor(d->hasPalette,dc);  	if ( lt==wDrawLineSolid ) {  		penStyle = PS_GEOMETRIC | PS_SOLID;  		if ( noFlatEndCaps == FALSE )  			penStyle |= PS_ENDCAP_FLAT; -		d->hPen = ExtCreatePen( penStyle, -				dw, -				&logBrush, -				0, -				NULL ); -				/*colorPalette.palPalEntry[dc] );*/ -	} else { -		d->hPen = CreatePen( PS_DOT, 0, mswGetColor( d->hasPalette, dc ) ); -	} -	hOldPen = SelectObject( hDc, d->hPen ); +	} else if (lt == wDrawLineDot) { +		penStyle = PS_GEOMETRIC | PS_DOT; +	} else if (lt == wDrawLineDash) { +		penStyle = PS_GEOMETRIC | PS_DASH; +	} else if (lt == wDrawLineDashDot) { +		penStyle = PS_GEOMETRIC | PS_DASHDOT; +	} else if ( lt == wDrawLineDashDotDot){ +		penStyle = PS_GEOMETRIC | PS_DASHDOTDOT; +	} else if (  lt == wDrawLineCenter) { +		penStyle = PS_GEOMETRIC | PS_USERSTYLE; +		penarray = ¢erPen; +		penarray_size = sizeof(centerPen)/sizeof(long); +	} else if (  lt == wDrawLinePhantom) { +		penStyle = PS_GEOMETRIC | PS_USERSTYLE; +		penarray = &phantomPen; +		penarray_size = sizeof(phantomPen) / sizeof(long); +	} else +		penStyle = PS_GEOMETRIC | PS_SOLID; +	d->hPen = ExtCreatePen( penStyle, +					dw, +					&logBrush, +					penarray_size, +					penarray ); +	hOldPen = SelectObject( d->hDc, d->hPen );  	DeleteObject( hOldPen );  }  static void setDrawBrush( -		HDC hDc,  		wDraw_p d,  		wDrawColor dc,  		wDrawOpts dopt ) @@ -200,7 +264,7 @@ static void setDrawBrush(  	static wDraw_p d0;  	static wDrawColor dc0 = -1; -	setDrawMode( hDc, d, 0, wDrawLineSolid, dc, dopt ); +	setDrawMode( d, 0, wDrawLineSolid, dc, dopt );  	if ( d == d0 && dc == dc0 )  		return; @@ -208,7 +272,7 @@ static void setDrawBrush(  	d->hBrush = CreateSolidBrush(   				mswGetColor(d->hasPalette,dc) ); -	hOldBrush = SelectObject( hDc, d->hBrush ); +	hOldBrush = SelectObject( d->hDc, d->hBrush );  	DeleteObject( hOldBrush );  } @@ -270,7 +334,7 @@ void wDrawLine(  {  	POINT p0, p1;  	RECT rect; -	setDrawMode( d->hDc, d, dw, lt, dc, dopt ); +	setDrawMode( d, dw, lt, dc, dopt );  	p0.x = XINCH2PIX(d,p0x);  	p0.y = YINCH2PIX(d,p0y);  	p1.x = XINCH2PIX(d,p1x); @@ -381,7 +445,7 @@ void wDrawArc(  	pe.x = XINCH2PIX(d,(wPos_t)pex);  	pe.y = YINCH2PIX(d,(wPos_t)pey); -	setDrawMode( d->hDc, d, dw, lt, dc, dopt ); +	setDrawMode( d, dw, lt, dc, dopt );  	if (dw == 0)  		dw = 1; @@ -495,7 +559,7 @@ void wDrawPoint(  		return;  	if ( p0.x >= d->w || p0.y >= d->h )  		return; -	setDrawMode( d->hDc, d, 0, wDrawLineSolid, dc, dopt ); +	setDrawMode( d, 0, wDrawLineSolid, dc, dopt );  	SetPixel( d->hDc, p0.x, p0.y, mswGetColor(d->hasPalette,dc) /*colorPalette.palPalEntry[dc]*/ );  	if (d->hWnd) { @@ -689,6 +753,7 @@ void wDrawGetTextSize(  		wPos_t *w,  		wPos_t *h,  		wPos_t *d, +		wPos_t *a,  		wDraw_p bd,  		const char * text,  		wFont_p fp, @@ -717,12 +782,25 @@ void wDrawGetTextSize(  	*w = XPIXELSTOINCH( bd, x );  	*h = YPIXELSTOINCH( bd, y );  	*d = YPIXELSTOINCH(bd, textMetric.tmDescent ); +	*a = YPIXELSTOINCH(bd, textMetric.tmAscent );  	SelectObject( bd->hDc, prevFont );  	DeleteObject( newFont );  	fp->lfHeight = oldLfHeight;  } - +/** + * Draw text + *  + * \param d	device context + * \param px position x + * \param py position y + * \param angle drawing angle + * \param text text to print + * \param fp font + * \param siz font size + * \param dc color + * \param dopts drawing options + */  void wDrawString(      wDraw_p d,      wPos_t px, @@ -736,8 +814,6 @@ void wDrawString(  {      int x, y;      HFONT newFont, prevFont; -    HDC newDc; -    HBITMAP oldBm, newBm;      DWORD extent;      int w, h;      RECT rect; @@ -756,61 +832,47 @@ void wDrawString(      y = YINCH2PIX(d,py) + (int)(mswcos(angle)*fp->lfHeight-0.5);      if (noNegDrawArgs > 0 && (x < 0 || y < 0)) { +		DeleteObject(newFont);          return;      } -    if (dopts & wDrawOptTemp) { -        setDrawMode(d->hDc, d, 0, wDrawLineSolid, dc, dopts); -        newDc = CreateCompatibleDC(d->hDc); -        prevFont = SelectObject(newDc, newFont); -        extent = GetTextExtent(newDc, CAST_AWAY_CONST text, strlen(text)); -        w = LOWORD(extent); -        h = HIWORD(extent); +		setDrawMode( d, 0, wDrawLineSolid, dc, dopts ); +		prevFont = SelectObject(d->hDc, newFont); +        SetBkMode(d->hDc, TRANSPARENT); -        if (h > w) { -            w = h; +        if (dopts & wDrawOutlineFont) { +            HPEN oldPen; +            BeginPath(d->hDc); +            TextOut(d->hDc, x, y, text, strlen(text)); +            EndPath(d->hDc); + +            // Now draw outline text +            oldPen = SelectObject(d->hDc, +                                  CreatePen(PS_SOLID, 1, +                                            mswGetColor(d->hasPalette, dc))); +            StrokePath(d->hDc); +            SelectObject(d->hDc, oldPen); +        } else { +            COLORREF old; + +            old = SetTextColor(d->hDc, mswGetColor(d->hasPalette, +                                                   dc)); +            TextOut(d->hDc, x, y, text, strlen(text)); +            SetTextColor(d->hDc, old);          } -        newBm = CreateCompatibleBitmap(d->hDc, w*2, w*2); -        oldBm = SelectObject(newDc, newBm); -        rect.top = rect.left = 0; -        rect.bottom = rect.right = w*2; -        FillRect(newDc, &rect, GetStockObject(WHITE_BRUSH)); -        TextOut(newDc, w, w, text, strlen(text)); -        BitBlt(d->hDc, x-w, y-w, w*2, w*2, newDc, 0, 0, tmpOp); -        SelectObject(newDc, oldBm); -        DeleteObject(newBm); -        SelectObject(newDc, prevFont); -        DeleteDC(newDc); - -        if (d->hWnd) { -            rect.top = y-(w+1); -            rect.bottom = y+(w+1); -            rect.left = x-(w+1); -            rect.right = x+(w+1); -            myInvalidateRect(d, &rect); -        } -    } else { -        COLORREF old; -        prevFont = SelectObject(d->hDc, newFont); -        SetBkMode(d->hDc, TRANSPARENT); -        old = SetTextColor(d->hDc, mswGetColor(d->hasPalette, -                                               dc)); -        TextOut(d->hDc, x, y, text, strlen(text)); -        SetTextColor(d->hDc, old);          extent = GetTextExtent(d->hDc, CAST_AWAY_CONST text, strlen(text));          SelectObject(d->hDc, prevFont);          w = LOWORD(extent);          h = HIWORD(extent);          if (d->hWnd) { -            rect.top = y-(w+h+1); -            rect.bottom = y+(w+h+1); -            rect.left = x-(w+h+1); -            rect.right = x+(w+h+1); +            rect.top = y - (w + h + 1); +            rect.bottom = y + (w + h + 1); +            rect.left = x - (w + h + 1); +            rect.right = x + (w + h + 1);              myInvalidateRect(d, &rect);          } -    }      DeleteObject(newFont);      fp->lfHeight = oldLfHeight; @@ -846,9 +908,9 @@ wFontSize_t wSelectedFontSize( void )  	return fontSize;  } -void wSetSelectedFontSize(int size) +void wSetSelectedFontSize(wFontSize_t size)  { -	fontSize = (wFontSize_t)size; +	fontSize = size;  }  /* @@ -870,10 +932,18 @@ void wDrawFilledRectangle(  		wDrawColor color,  		wDrawOpts opts )  { +	int mode;  	RECT rect;  	if (d == NULL)  		return; -	setDrawBrush( d->hDc, d, color, opts ); +	setDrawBrush( d, color, opts ); +	if (opts & wDrawOptTransparent) { +		mode = R2_NOTXORPEN; +	} +	else { +		mode = R2_COPYPEN; +	} +	SetROP2(d->hDc, mode);  	rect.left = XINCH2PIX(d,px);  	rect.right = XINCH2PIX(d,px+sx);  	rect.top = YINCH2PIX(d,py+sy); @@ -903,103 +973,230 @@ void wDrawFilledRectangle(  }  #ifdef DRAWFILLPOLYLOG -static FILE * logF; +    static FILE * logF;  #endif -static int wFillPointsMax = 0; -static POINT * wFillPoints; + +static dynArr_t wFillPoints_da; +static dynArr_t wFillType_da; + +#define POINTTYPE(N) DYNARR_N( BYTE, wFillType_da, (N) ) +#define POINTPOS(N) DYNARR_N( POINT, wFillPoints_da, (N) ) + +/** + * Add a point definition to the list. The clipping rectangle is recalculated to + * include the new point. + * + * \param d IN drawing context + * \param pk IN index of new point + * \param pp IN pointer to the point's coordinates + * \param type IN line type + * \param pr IN/OUT clipping rectangle + */  static void addPoint( -		int * pk, -		POINT * pp, -		RECT * pr ) +    wDraw_p d, +    int pk, +    coOrd * pp, +    BYTE type, RECT * pr)  { +    POINT p; +    p.x = XINCH2PIX(d, pp->x); +    p.y = YINCH2PIX(d, pp->y); +  #ifdef DRAWFILLPOLYLOG -fprintf( logF, "	q[%d] = {%d,%d}\n", *pk, pp->x, pp->y ); +    fprintf(logF, "	q[%d] = {%d,%d}\n", pk, p.x, p.y);  #endif -	if ( *pk > 0 && -		 wFillPoints[(*pk)-1].x == pp->x && wFillPoints[(*pk)-1].y == pp->y ) -		return; -	wFillPoints[ (*pk)++ ] = *pp; -	if (pp->x<pr->left) -		pr->left = pp->x; -	if (pp->x>pr->right) -		pr->right = pp->x; -	if (pp->y<pr->top) -		pr->top = pp->y; -	if (pp->y>pr->bottom) -		pr->bottom = pp->y; + +    DYNARR_N(POINT, wFillPoints_da, pk) = p; +    DYNARR_N(BYTE, wFillType_da, pk) = type; + +    if (p.x < pr->left) { +        pr->left = p.x; +    } +    if (p.x > pr->right) { +        pr->right = p.x; +    } +    if (p.y < pr->top) { +        pr->top = p.y; +    } +    if (p.y > pr->bottom) { +        pr->bottom = p.y; +    }  } -void wDrawFilledPolygon( -		wDraw_p d, -		wPos_t p[][2], -		int cnt, -		wDrawColor color, -		wDrawOpts opts ) -{				  -	RECT rect; -	int i, k; -	POINT p0, p1, q0, q1; -	static POINT zero = { 0, 0 }; -	wBool_t p1Clipped; +/** + * Draw a polyline consisting of straights with smoothed or rounded corners. + * Optionally the area can be filled. + * + * \param d	IN	drawing context + * \param node IN 2 dimensional array of coordinates + * \param type IN type of corener (vertex, smooth or round) + * \param cnt IN number of points + * \param color IN color + * \param dw IN line width + * \param lt IN line type + * \param opts IN drawing options + * \param fill IN area will be filled if true + * \param open IN do not close area + */ -	if (d == NULL) -		return; -	if (cnt*2 > wFillPointsMax) { -		wFillPoints = realloc( wFillPoints, cnt * 2 * sizeof *(POINT*)NULL ); -		if (wFillPoints == NULL) { -			fputs("can't realloc wFillPoints\n", stderr); -			abort(); +void wDrawPolygon( +    wDraw_p d, +    wPos_t node[][2], +    wPolyLine_e type[], +    wIndex_t cnt, +    wDrawColor color, +    wDrawWidth dw, +    wDrawLineType_e lt, +    wDrawOpts opts, +    int fill, +    int open) +{ +    RECT rect; +    int i, prevNode, nextNode; +    int pointCount = 0; +    coOrd endPoint0, endPoint1, controlPoint0, controlPoint1; +    coOrd point, startingPoint; +    BOOL rc; +    int closed = 0; + +    if (d == NULL) { +        return; +    } + +    // make sure the array for the points is large enough +    // worst case are rounded corners that require 4 points +    DYNARR_RESET(POINT,wFillPoints_da); +    DYNARR_SET(POINT,wFillPoints_da,(cnt + 1) * 4); +    DYNARR_RESET(BYTE,wFillType_da); +    DYNARR_SET(POINT,wFillType_da, (cnt + 1) * 4); + +    BeginPath(d->hDc); + +    if (fill) { +		int mode; +        setDrawBrush(d, color, opts); +		if (opts & wDrawOptTransparent) { +			mode = R2_NOTXORPEN;  		} -		wFillPointsMax = cnt*2; -	} -	setDrawBrush( d->hDc, d, color, opts ); -	p1.x = rect.left = rect.right = XINCH2PIX(d,p[cnt-1][0]-1); -	p1.y = rect.top = rect.bottom = YINCH2PIX(d,p[cnt-1][1]+1); -#ifdef DRAWFILLPOLYLOG -logF = fopen( "log.txt", "a" ); -fprintf( logF, "\np[%d] = {%d,%d}\n", cnt-1, p1.x, p1.y ); -#endif -	p1Clipped = FALSE; -	for ( i=k=0; i<cnt; i++ ) { -		p0 = p1; -		p1.x = XINCH2PIX(d,p[i][0]-1); -		p1.y = YINCH2PIX(d,p[i][1]+1); -#ifdef DRAWFILLPOLYLOG -fprintf( logF, "p[%d] = {%d,%d}\n", i, p1.x, p1.y ); -#endif -		q0 = p0; -		q1 = p1; -		if ( clip0( &q0, &q1, NULL ) ) { -#ifdef DRAWFILLPOLYLOG -fprintf( logF, "  clip( {%d,%d} {%d,%d} )  = {%d,%d} {%d,%d}\n", p0.x, p0.y, p1.x, p1.y, q0.x, q0.y, q1.x, q1.y ); -#endif -			if ( q0.x != p0.x || q0.y != p0.y ) { -				if ( k > 0 && ( q0.x > q0.y ) != ( wFillPoints[k-1].x > wFillPoints[k-1].y ) ) -					 addPoint( &k, &zero, &rect ); -				addPoint( &k, &q0, &rect ); -			} -			addPoint( &k, &q1, &rect ); -			p1Clipped = ( q1.x != p1.x || q1.y != p1.y ); +		else { +			mode = R2_COPYPEN;  		} -	} -	if ( p1Clipped && -		 ( wFillPoints[k-1].x > wFillPoints[k-1].y ) != ( wFillPoints[0].x > wFillPoints[0].y ) ) -		addPoint( &k, &zero, &rect ); +		SetROP2(d->hDc, mode); + +    } else { +        setDrawMode(d, dw, lt, color, opts); +    } + +    rect.left = rect.right = XINCH2PIX(d,node[cnt-1][0]-1); +    rect.top = rect.bottom = YINCH2PIX(d,node[cnt-1][1]+1); +  #ifdef DRAWFILLPOLYLOG -fflush( logF ); -fclose( logF ); +    logF = fopen("log.txt", "a"); +    fprintf(logF, "\np[%d] = {%d,%d}\n", cnt-1, node[0][0], node[0][1]);  #endif -	if ( k <= 2 ) -		return; -	Polygon( d->hDc, wFillPoints, k ); -	if (d->hWnd) { -		rect.top--; -		rect.left--; -		rect.bottom++; -		rect.right++; -		myInvalidateRect( d, &rect ); -	} + +    for (i=0; i<cnt; i++) { +        wPolyLine_e type1; +        point.x = node[i][0]; +        point.y = node[i][1]; +		if (type != NULL) +			type1 = type[i]; +		else +			type1 = wPolyLineStraight; + +        if (type1 == wPolyLineRound || type1 == wPolyLineSmooth) { +            prevNode = (i == 0) ? cnt - 1 : i - 1; +            nextNode = (i == cnt - 1) ? 0 : i + 1; + +            // calculate distance to neighboring nodes +            int prevXDistance = node[i][0] - node[prevNode][0]; +            int prevYDistance = node[i][1] - node[prevNode][1]; +            int nextXDistance = node[nextNode][0]-node[i][0]; +            int nextYDistance = node[nextNode][1]-node[i][1]; + +            // distance from node to endpoints of curve is half the line length +            endPoint0.x = (prevXDistance/2)+node[prevNode][0]; +            endPoint0.y = (prevYDistance/2)+node[prevNode][1]; +            endPoint1.x = (nextXDistance/2)+node[i][0]; +            endPoint1.y = (nextYDistance/2)+node[i][1]; + +            if (type1 == wPolyLineRound) { +                double distNext = (nextXDistance*nextXDistance + nextYDistance * nextYDistance); +                double distPrev = (prevXDistance*prevXDistance + prevYDistance * prevYDistance); +                // but should be half of the shortest line length (equidistant from node) for round +                if ((distPrev > 0) && (distNext > 0)) { +                    double ratio = sqrt(distPrev / distNext); +                    if (distPrev < distNext) { +                        endPoint1.x = ((nextXDistance*ratio) / 2) + node[i][0]; +                        endPoint1.y = ((nextYDistance*ratio) / 2) + node[i][1]; +                    } else { +                        endPoint0.x = node[i][0] - (prevXDistance / (2 * ratio)); +                        endPoint0.y = node[i][1] - (prevYDistance / (2 * ratio)); +                    } +                } +                // experience says that the best look is achieved if the +                // control points are in the middle between end point and node +                controlPoint0.x = (node[i][0] - endPoint0.x) / 2 + endPoint0.x; +                controlPoint0.y = (node[i][1] - endPoint0.y) / 2 + endPoint0.y; + +                controlPoint1.x = (endPoint1.x - node[i][0]) / 2 + node[i][0]; +                controlPoint1.y = (endPoint1.y - node[i][1]) / 2 + node[i][1]; +            } else { +                controlPoint0 = point; +                controlPoint1 = point; +            } +        } + +        if (i==0) { +            if (type1 == wPolyLineStraight || open) { +                // for straight lines or open shapes use the starting point as passed +                addPoint(d, pointCount++, &point, PT_MOVETO, &rect); +                startingPoint = point; +            } else { +                // for Bezier begin with the calculated starting point +                addPoint(d, pointCount++, &endPoint0, PT_MOVETO, &rect); +                addPoint(d, pointCount++, &controlPoint0, PT_BEZIERTO, &rect); +                addPoint(d, pointCount++, &controlPoint1, PT_BEZIERTO, &rect); +                addPoint(d, pointCount++, &endPoint1, PT_BEZIERTO, &rect); +                startingPoint = endPoint0; +            } +        } else { +            if (type1 == wPolyLineStraight || (open && (i==cnt-1))) { +                addPoint(d, pointCount++, &point, PT_LINETO, &rect); +            } else { +                if (i==cnt-1 && !open) { +                    closed = TRUE; +                } +                addPoint(d, pointCount++, &endPoint0, PT_LINETO, &rect); +                addPoint(d, pointCount++, &controlPoint0, PT_BEZIERTO, &rect); +                addPoint(d, pointCount++, &controlPoint1, PT_BEZIERTO, &rect); +                addPoint(d, pointCount++, &endPoint1, +                         PT_BEZIERTO | (closed ? PT_CLOSEFIGURE : 0), &rect); +            } +        } +    } + +    if (!open && !closed) { +        addPoint(d, pointCount++, &startingPoint, PT_LINETO, &rect); +    } +    rc = PolyDraw(d->hDc, wFillPoints_da.ptr, wFillType_da.ptr, pointCount); + +    EndPath(d->hDc); + +    if (fill && !open) { +        FillPath(d->hDc); +    } else { +        StrokePath(d->hDc); +    } + +    if (d->hWnd) { +        rect.top--; +        rect.left--; +        rect.bottom++; +        rect.right++; +        myInvalidateRect(d, &rect); +    }  }  #define MAX_FILLCIRCLE_POINTS	(30) @@ -1022,7 +1219,7 @@ void wDrawFilledCircle(  	p1.x = XINCH2PIX(d,x+r);  	p1.y = YINCH2PIX(d,y-r)+1; -	setDrawBrush( d->hDc, d, color, opts );						   +	setDrawBrush( d, color, opts );						    	if ( noNegDrawArgs > 0 && ( p0.x < 0 || p0.y < 0 ) ) {  		if ( r > MAX_FILLCIRCLE_POINTS )  			cnt = MAX_FILLCIRCLE_POINTS; @@ -1035,7 +1232,8 @@ void wDrawFilledCircle(  			circlePts[inx][0] = x + (int)(r * mswcos( inx*dang ) + 0.5 );  			circlePts[inx][1] = y + (int)(r * mswsin( inx*dang ) + 0.5 );  		} -		wDrawFilledPolygon( d, circlePts, cnt, color, opts ); +		//wDrawFilledPolygon( d, circlePts, NULL, cnt, color, opts ); +		wDrawPolygon(d, circlePts, NULL, cnt, color, 1, wDrawLineSolid,opts, TRUE, FALSE );  	} else {  		Ellipse( d->hDc, p0.x, p0.y, p1.x, p1.y );  		if (d->hWnd) { @@ -1084,21 +1282,30 @@ void wDrawRestoreImage(  } -void wDrawClear( wDraw_p d ) +void wDrawClearTemp( wDraw_p d )  {  	RECT rect; -	SetROP2( d->hDc, R2_WHITE ); -	Rectangle( d->hDc, 0, 0, d->w, d->h ); +	SelectObject( d->hDc, d->hBmTemp ); +	BitBlt(d->hDc, 0, 0, d->w, d->h, d->hDc, 0, 0, WHITENESS);  	if (d->hWnd) { -	rect.top = 0; -	rect.bottom = d->h; -	rect.left = 0; -	rect.right = d->w; -	InvalidateRect( d->hWnd, &rect, FALSE ); +		rect.top = 0; +		rect.bottom = d->h; +		rect.left = 0; +		rect.right = d->w; +		InvalidateRect( d->hWnd, &rect, FALSE );  	}  } +void wDrawClear( wDraw_p d ) +{ +	SelectObject( d->hDc, d->hBmMain ); +	// BitBlt is faster than Rectangle +	BitBlt(d->hDc, 0, 0, d->w, d->h, d->hDc, 0, 0, WHITENESS); +	wDrawClearTemp(d); +} + +  void wDrawSetSize(  		wDraw_p d,  		wPos_t width, @@ -1196,7 +1403,7 @@ void wDrawBitMap(  		wDrawColor dc,  		wDrawOpts dopt )  { -	HDC bmDc, hDc; +	HDC bmDc;  	HBITMAP oldBm;  	DWORD mode;  	int x0, y0; @@ -1208,9 +1415,7 @@ void wDrawBitMap(  	if ( noNegDrawArgs > 0 && ( x0 < 0 || y0 < 0 ) )  		return;  #endif -	if (dopt & wDrawOptTemp) { -		mode = tmpOp; -	} else if (dc == wDrawColorWhite) { +	if (dc == wDrawColorWhite) {  		mode = clrOp;  		dc = wDrawColorBlack;  	} else { @@ -1224,22 +1429,9 @@ void wDrawBitMap(  				RGB( 255, 255, 255 ), bm->w, bm->h, bm->bmx );  		bm->color = dc;  	} -	if ( (dopt & wDrawOptNoClip) != 0 && -		 ( px < 0 || px >= d->w || py < 0 || py >= d->h ) ) { -		x0 += d->x; -		y0 += d->y; -		hDc = GetDC( ((wControl_p)(d->parent))->hWnd ); -		bmDc = CreateCompatibleDC( hDc ); -		oldBm = SelectObject( bmDc, bm->bm ); -		BitBlt( hDc, x0, y0, bm->w, bm->h, bmDc, 0, 0, tmpOp ); -		SelectObject( bmDc, oldBm ); -		DeleteDC( bmDc ); -		ReleaseDC( ((wControl_p)(d->parent))->hWnd, hDc ); -		return; -	}  	bmDc = CreateCompatibleDC( d->hDc ); -	setDrawMode( d->hDc, d, 0, wDrawLineSolid, dc, dopt ); +	setDrawMode( d, 0, wDrawLineSolid, dc, dopt );  	oldBm = SelectObject( bmDc, bm->bm );  	BitBlt( d->hDc, x0, y0, bm->w, bm->h, bmDc, 0, 0, mode );  	SelectObject( bmDc, oldBm ); @@ -1260,7 +1452,7 @@ wDrawBitMap_p wDrawBitMapCreate(  		int h,  		int x,  		int y, -		const char * bits ) +		const unsigned char * bits )  {  	wDrawBitMap_p bm;  	int bmSize = ((w+7)/8) * h; @@ -1322,12 +1514,14 @@ long FAR PASCAL XEXPORT mswDrawPush(  		hDc = GetDC(hWnd);  		if ( b->option & BD_DIRECT ) {  			b->hDc = hDc; -			b->hBm = 0; +			b->hBmMain = 0; +			b->hBmTemp = 0;  			b->hBmOld = 0;  		} else {  			b->hDc = CreateCompatibleDC( hDc );  -			b->hBm = CreateCompatibleBitmap( hDc, b->w, b->h ); -			b->hBmOld = SelectObject( b->hDc, b->hBm ); +			b->hBmMain = CreateCompatibleBitmap( hDc, b->w, b->h ); +			b->hBmTemp = CreateCompatibleBitmap( hDc, b->w, b->h ); +			b->hBmOld = SelectObject( b->hDc, b->hBmMain );  		}  		if (mswPalette) {  			SelectPalette( b->hDc, mswPalette, 0 ); @@ -1355,8 +1549,12 @@ long FAR PASCAL XEXPORT mswDrawPush(  			if ( b->option & BD_DIRECT ) {  			} else {  			hDc = GetDC( b->hWnd ); -			b->hBm = CreateCompatibleBitmap( hDc, b->w, b->h ); -			DeleteObject(SelectObject( b->hDc, b->hBm )); +//-			DeleteObject( b->hBmOld ); +			DeleteObject( b->hBmMain ); +			DeleteObject( b->hBmTemp ); +			b->hBmMain = CreateCompatibleBitmap( hDc, b->w, b->h ); +			b->hBmTemp = CreateCompatibleBitmap( hDc, b->w, b->h ); +//-			b->hBmOld = SelectObject( b->hDc, b->hBmMain );  			ReleaseDC( b->hWnd, hDc );  			SetROP2( b->hDc, R2_WHITE );  			Rectangle( b->hDc, 0, 0, b->w, b->h ); @@ -1383,6 +1581,7 @@ long FAR PASCAL XEXPORT mswDrawPush(  	case WM_LBUTTONUP:  	case WM_RBUTTONDOWN:  	case WM_RBUTTONUP: +	case WM_LBUTTONDBLCLK:  		if (message == WM_LBUTTONDOWN)  			action = wActionLDown;  		else if (message == WM_RBUTTONDOWN) @@ -1391,6 +1590,8 @@ long FAR PASCAL XEXPORT mswDrawPush(  			action = wActionLUp;  		else if (message == WM_RBUTTONUP)  			action = wActionRUp; +		else if (message == WM_LBUTTONDBLCLK) +			action = wActionLDownDouble;  		else {  			if ( (wParam & MK_LBUTTON) != 0)  				action = wActionLDrag; @@ -1414,6 +1615,8 @@ long FAR PASCAL XEXPORT mswDrawPush(  		iy = HIWORD( lParam );  		x = XPIX2INCH( b, ix );  		y = YPIX2INCH( b, iy ); +		b->lastX = x; +		b->lastY = y;  		if (b->action)  			b->action( b, b->data, action, x, y );  		if (b->hWnd) @@ -1435,7 +1638,7 @@ long FAR PASCAL XEXPORT mswDrawPush(  		case VK_RIGHT:	extChar = wAccelKey_Right; break;				  		case VK_LEFT:	extChar = wAccelKey_Left; break;  		case VK_BACK:	extChar = wAccelKey_Back; break; -		/*case VK_F1:	extChar = wAccelKey_F1; break;*/ +		case VK_F1:		extChar = wAccelKey_F1; break;  		case VK_F2:		extChar = wAccelKey_F2; break;  		case VK_F3:		extChar = wAccelKey_F3; break;  		case VK_F4:		extChar = wAccelKey_F4; break; @@ -1450,9 +1653,9 @@ long FAR PASCAL XEXPORT mswDrawPush(  		}  		if (b && b->action) {  			if (extChar != wAccelKey_None) -				b->action( b, b->data, wActionExtKey + ( (int)extChar << 8 ), 0, 0 ); +				b->action( b, b->data, wActionExtKey + ( (int)extChar << 8 ), b->lastX, b->lastY );  			else -				b->action( b, b->data, wActionText + ( wParam << 8 ), 0, 0 ); +				b->action( b, b->data, wActionText + ( wParam << 8 ), b->lastX, b->lastY );  		}  		return 0; @@ -1468,11 +1671,22 @@ long FAR PASCAL XEXPORT mswDrawPush(  						b->paletteClock = winPaletteClock;  					}  				} +				HBITMAP hBmOld = SelectObject( b->hDc, b->hBmMain ); + +			if (bDrawMainBM) { +				BitBlt(hDc, rect.left, rect.top, +					rect.right - rect.left, rect.bottom - rect.top, +					b->hDc, rect.left, rect.top, +					SRCCOPY); +			} +				SelectObject( b->hDc, b->bCopiedMain?b->hBmTemp:b->hBmMain );  				BitBlt( hDc, rect.left, rect.top,  						rect.right-rect.left, rect.bottom-rect.top,  						b->hDc, rect.left, rect.top, -						SRCCOPY ); +						bDrawMainBM?SRCAND:SRCCOPY); +				SelectObject( b->hDc, hBmOld );  				EndPaint( hWnd, &ps ); +				b->bCopiedMain = FALSE;  			}  		}  		break; @@ -1499,18 +1713,44 @@ long FAR PASCAL XEXPORT mswDrawPush(  static LRESULT drawMsgProc( wDraw_p b, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )  {  	wAction_t action; - +	  	switch( message ) {  	case WM_MOUSEWHEEL:  		/* handle mouse wheel events */ -		/* fwKeys = GET_KEYSTATE_WPARAM(wParam); modifier keys are currently ignored */ -		if ( GET_WHEEL_DELTA_WPARAM(wParam) > 0 ) { -			action = wActionWheelUp; +		if (GET_KEYSTATE_WPARAM(wParam) & (MK_SHIFT|MK_MBUTTON) ) { +			if (GET_KEYSTATE_WPARAM(wParam) & MK_CONTROL ) { +				if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { +					action = wActionScrollLeft; +				} else { +					action = wActionScrollRight; +				} +			} else { +				if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { +					action = wActionScrollUp; +				} else { +					action = wActionScrollDown; +				} +			}  		} else { -			action = wActionWheelDown; +			if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { +				action = wActionWheelUp; +			} else { +				action = wActionWheelDown; +			} +		} +		if (b->action) +			b->action( b, b->data, action, b->lastX, b->lastY ); +		return 0; +	case WM_MOUSEHWHEEL: +		if ( GET_KEYSTATE_WPARAM(wParam) & (MK_SHIFT|MK_MBUTTON)) { +			if ( GET_WHEEL_DELTA_WPARAM(wParam) > 0 ) { +				action = wActionScrollRight; +			} else { +				action = wActionScrollLeft; +			}  		}  		if (b->action) -			b->action( b, b->data, action, 0, 0 ); +			b->action( b, b->data, action, b->lastX, b->lastY );  		return 0;  	} @@ -1521,10 +1761,12 @@ static LRESULT drawMsgProc( wDraw_p b, HWND hWnd, UINT message, WPARAM wParam, L  static void drawDoneProc( wControl_p b )  {  	wDraw_p d = (wDraw_p)b; -	if (d->hBm) { +	if (d->hBmMain) {  		SelectObject( d->hDc, d->hBmOld ); -		DeleteObject( d->hBm ); -		d->hBm = (HBITMAP)0; +		DeleteObject( d->hBmMain ); +		d->hBmMain = (HBITMAP)0; +		DeleteObject( d->hBmTemp ); +		d->hBmTemp = (HBITMAP)0;  	}  	if (d->hPen) {  		SelectObject( d->hDc, GetStockObject( BLACK_PEN ) ); @@ -1580,10 +1822,17 @@ void mswRepaintAll( void )  	for ( b=drawList; b; b=b->drawNext ) {  		if (GetUpdateRect( b->hWnd, &rect, FALSE )) {  			hDc = BeginPaint( b->hWnd, &ps ); +			HBITMAP hBmOld = SelectObject( b->hDc, b->hBmMain );  			BitBlt( hDc, rect.left, rect.top,  						rect.right-rect.left, rect.bottom-rect.top,  						b->hDc, rect.left, rect.top,  						SRCCOPY ); +			SelectObject( b->hDc, b->hBmTemp ); +			BitBlt( hDc, rect.left, rect.top, +						rect.right-rect.left, rect.bottom-rect.top, +						b->hDc, rect.left, rect.top, +						SRCAND ); +			SelectObject( b->hDc, hBmOld );  			EndPaint( b->hWnd, &ps );  		}  	} @@ -1648,6 +1897,7 @@ wDraw_p wDrawCreate(  		SelectPalette( hDc, mswPalette, 0 );  		ReleaseDC( d->hWnd, hDc );  	} +	d->bCopiedMain = FALSE;  	return d;  } @@ -1681,14 +1931,19 @@ wDraw_p wBitMapCreate( wPos_t w, wPos_t h, int planes )  		wNoticeEx( NT_ERROR, "CreateBitMap: CreateDC fails", "Ok", NULL );  		return FALSE;  	} -	d->hBm = CreateCompatibleBitmap( hDc, d->w, d->h ); -	if ( d->hBm == (HBITMAP)0 ) { -		wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM fails", "Ok", NULL ); +	d->hBmMain = CreateCompatibleBitmap( hDc, d->w, d->h ); +	if ( d->hBmMain == (HBITMAP)0 ) { +		wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM Main fails", "Ok", NULL ); +		return FALSE; +	} +	d->hBmTemp = CreateCompatibleBitmap( hDc, d->w, d->h ); +	if ( d->hBmTemp == (HBITMAP)0 ) { +		wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM Temp fails", "Ok", NULL );  		return FALSE;  	}  	d->hasPalette = (GetDeviceCaps(hDc,RASTERCAPS ) & RC_PALETTE) != 0;  	ReleaseDC( mswHWnd, hDc ); -	d->hBmOld = SelectObject( d->hDc, d->hBm ); +	d->hBmOld = SelectObject( d->hDc, d->hBmMain );  	if (mswPalette) {  		SelectPalette( d->hDc, mswPalette, 0 );  		RealizePalette( d->hDc ); @@ -1697,8 +1952,9 @@ wDraw_p wBitMapCreate( wPos_t w, wPos_t h, int planes )  	d->hFactor = (double)GetDeviceCaps( d->hDc, LOGPIXELSY );  	d->DPI = 96.0; /*min( d->wFactor, d->hFactor );*/  	d->hWnd = 0; -	SetROP2( d->hDc, R2_WHITE ); -	Rectangle( d->hDc, 0, 0, d->w, d->h ); +	wDrawClear(d); +//-	SetROP2( d->hDc, R2_WHITE ); +//-	Rectangle( d->hDc, 0, 0, d->w, d->h );  	return d;  } @@ -1709,10 +1965,12 @@ wBool_t wBitMapDelete( wDraw_p d )  		DeleteObject( d->hPen );  		d->hPen = (HPEN)0;  	} -	if (d->hBm) { +	if (d->hBmMain) {  		SelectObject( d->hDc, d->hBmOld ); -		DeleteObject( d->hBm ); -		d->hBm = (HBITMAP)0; +		DeleteObject( d->hBmMain ); +		d->hBmMain = (HBITMAP)0; +		DeleteObject( d->hBmTemp ); +		d->hBmTemp = (HBITMAP)0;  	}  	if (d->hDc) {  		DeleteDC( d->hDc ); @@ -1722,74 +1980,75 @@ wBool_t wBitMapDelete( wDraw_p d )  	return TRUE;  } -wBool_t wBitMapWriteFile( wDraw_p d, const char * fileName ) +/** + * write bitmap file. The bitmap in d must contain a valid HBITMAP + * + * \param  d	    A wDraw_p to process. + * \param  fileName Filename of the file. + * + * \returns A wBool_t. TRUE on success + */ + +wBool_t +wBitMapWriteFile(wDraw_p d, const char * fileName)  { -	char *pixels; -	int j, ww, chunk; -	FILE * f; -	BITMAPFILEHEADER bmfh; -	struct { -		BITMAPINFOHEADER bmih; -		RGBQUAD colors[256]; -	} bmi; -	int rc; -	 -	if ( d->hBm == 0) -		return FALSE; -	f = wFileOpen( fileName, "wb" ); -	if (!f) { -		wNoticeEx( NT_ERROR, fileName, "Ok", NULL ); -		return FALSE; -	} -	ww = ((d->w +3) / 4) * 4; -	bmfh.bfType = 'B'+('M'<<8); -	bmfh.bfSize = (long)(sizeof bmfh) + (long)(sizeof bmi.bmih) + (long)(sizeof bmi.colors) + (long)ww * (long)(d->h); -	bmfh.bfReserved1 = 0; -	bmfh.bfReserved2 = 0; -	bmfh.bfOffBits = sizeof bmfh + sizeof bmi.bmih + sizeof bmi.colors; -	fwrite( &bmfh, 1, sizeof bmfh, f ); -	bmi.bmih.biSize = sizeof bmi.bmih; -	bmi.bmih.biWidth = d->w; -	bmi.bmih.biHeight = d->h; -	bmi.bmih.biPlanes = 1; -	bmi.bmih.biBitCount = 8; -	bmi.bmih.biCompression = BI_RGB; -	bmi.bmih.biSizeImage = 0; -	bmi.bmih.biXPelsPerMeter = 75*(10000/254); -	bmi.bmih.biYPelsPerMeter = 75*(10000/254); -	bmi.bmih.biClrUsed = bmi.bmih.biClrImportant = mswGetColorList( bmi.colors ); -	SelectObject( d->hDc, d->hBmOld ); -	rc = GetDIBits( d->hDc, d->hBm, 0, 1, NULL, (BITMAPINFO*)&bmi, DIB_RGB_COLORS ); -	if ( rc == 0 ) { -		wNoticeEx( NT_ERROR, "WriteBitMap: Can't get bitmapinfo from Bitmap", "Ok", NULL ); -		return FALSE; -	} -	bmi.bmih.biClrUsed = 256; -	fwrite( &bmi.bmih, 1, sizeof bmi.bmih, f ); -	fwrite( bmi.colors, 1, sizeof bmi.colors, f ); -	chunk = 32000/ww; -	pixels = (char*)malloc( ww*chunk ); -	if ( pixels == NULL ) { -		wNoticeEx( NT_ERROR, "WriteBitMap: no memory", "OK", NULL ); -		return FALSE; -	} -	for (j=0;j<d->h;j+=chunk) { -		if (j+chunk>d->h) -			chunk = d->h-j; -		rc = GetDIBits( d->hDc, d->hBm, j, chunk, pixels, (BITMAPINFO*)&bmi, DIB_RGB_COLORS ); -		if ( rc == 0 )  -		if ( rc == 0 ) { -			wNoticeEx( NT_ERROR, "WriteBitMap: Can't get bits from Bitmap", "Ok", NULL ); -			return FALSE; -		} -		rc = fwrite( pixels, 1, ww*chunk, f ); -		if (rc != ww*chunk) { -			wNoticeEx( NT_ERROR, "WriteBitMap: Bad fwrite", "Ok", NULL); -		} -	} -	free( pixels ); -	SelectObject( d->hDc, d->hBm ); -	fclose( f ); -	return TRUE; +    FIBITMAP *dib = NULL; +    FIBITMAP *dib2 = NULL; +    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; +    BOOL bSuccess = FALSE; + +    if (d->hBmMain) { + +        BITMAP bm; +        GetObject(d->hBmMain, sizeof(BITMAP), (LPSTR)&bm); +        dib = FreeImage_Allocate(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, 0, 0, 0); +        // The GetDIBits function clears the biClrUsed and biClrImportant BITMAPINFO members (dont't know why) +        // So we save these infos below. This is needed for palettized images only. +        int nColors = FreeImage_GetColorsUsed(dib); +        HDC dc = GetDC(NULL); +        GetDIBits(dc, +                  d->hBmMain, +                  0, +                  FreeImage_GetHeight(dib), +                  FreeImage_GetBits(dib), +                  FreeImage_GetInfo(dib), +                  DIB_RGB_COLORS); +        ReleaseDC(NULL, dc); + +        // restore BITMAPINFO members +        FreeImage_GetInfoHeader(dib)->biClrUsed = nColors; +        FreeImage_GetInfoHeader(dib)->biClrImportant = nColors; +        // we will get a 32 bit bitmap on Windows systems with invalid alpha +        // so it needs to be converted to 24 bits. +        // (see: https://sourceforge.net/p/freeimage/discussion/36110/thread/0699ce8e/ ) +        dib2 = FreeImage_ConvertTo24Bits(dib); +        FreeImage_Unload(dib); +    } + +    // Try to guess the file format from the file extension +    fif = FreeImage_GetFIFFromFilename(fileName); +    if (fif != FIF_UNKNOWN) { +        // Check that the dib can be saved in this format +        BOOL bCanSave; + +        FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib2); +        if (image_type == FIT_BITMAP) { +            // standard bitmap type +            WORD bpp = FreeImage_GetBPP(dib2); +            bCanSave = (FreeImage_FIFSupportsWriting(fif) && +                        FreeImage_FIFSupportsExportBPP(fif, bpp)); +        } else { +            // special bitmap type +            bCanSave = FreeImage_FIFSupportsExportType(fif, image_type); +        } + +        if (bCanSave) { +            bSuccess = FreeImage_Save(fif, dib2, fileName, PNG_DEFAULT); +            return bSuccess; +        } +    } +    FreeImage_Unload(dib2); + +    return bSuccess;  } diff --git a/app/wlib/mswlib/mswedit.c b/app/wlib/mswlib/mswedit.c index fbae89f..dc70ac3 100644 --- a/app/wlib/mswlib/mswedit.c +++ b/app/wlib/mswlib/mswedit.c @@ -1,3 +1,25 @@ +/** \file mswedit.c + * Text entry widgets + */ +  +/*  XTrackCAD - Model Railroad CAD + *  Copyright (C) 2005 Dave Bullis + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +   #include <windows.h>  #include <string.h>  #include <malloc.h> @@ -15,6 +37,7 @@ struct wString_t {  		wStringCallBack_p action;  		}; +#ifdef LATER  struct wInteger_t {  		WOBJ_COMMON  		long low, high; @@ -30,6 +53,7 @@ struct wFloat_t {  		double oldValue;  		wFloatCallBack_p action;  		}; +#endif // LATER  static XWNDPROC oldEditProc = NULL; @@ -47,53 +71,35 @@ long FAR PASCAL _export pushEdit(  		UINT wParam,  		LONG lParam )  { -	/* Catch <Return> and cause focus to leave control */ +  #ifdef WIN32  	long inx = GetWindowLong( hWnd, GWL_ID );  #else  	short inx = GetWindowWord( hWnd, GWW_ID );  #endif -	wControl_p b = mswMapIndex( inx ); +	wControl_p b = mswMapIndex(inx); -	switch (message) { +	switch (message) +	{  	case WM_CHAR: -		if ( b != NULL) { -			switch( wParam ) { -			case 0x0D: -			case 0x1B: -			case 0x09: -				SetFocus( ((wControl_p)(b->parent))->hWnd );  -				SendMessage( ((wControl_p)(b->parent))->hWnd, WM_CHAR, -						wParam, lParam ); -				/*SendMessage( ((wControl_p)(b->parent))->hWnd, WM_COMMAND, -						inx, MAKELONG( hWnd, EN_KILLFOCUS ) );*/ -				return 0L; -			} -		} -		break; - -	case WM_KEYUP: -		if ( b != NULL) -			switch (b->type) { -			case B_STRING: -				if (((wString_p)b)->action) -					mswSetTrigger( (wControl_p)b, triggerString ); -				break; -#ifdef LATER -			case B_INTEGER: -				if (((wInteger_p)b)->action) -					mswSetTrigger( (wControl_p)b, triggerInteger ); -				break; -			case B_FLOAT: -				if (((wFloat_p)b)->action) -					mswSetTrigger( (wControl_p)b, triggerFloat ); -				break; -#endif -			} -		break; +	    if (b != NULL) { +	        switch (wParam) { +	        case VK_RETURN: +	            triggerString(b); +	            return (0L); +	            break; +	        case 0x1B: +	        case 0x09: +	            SetFocus(((wControl_p)(b->parent))->hWnd); +	            SendMessage(((wControl_p)(b->parent))->hWnd, WM_CHAR, +	                        wParam, lParam); +	            return 0L; +	        } +	    } +	    break;  	} -	return CallWindowProc( oldEditProc, hWnd, message, wParam, lParam ); +	return CallWindowProc(oldEditProc, hWnd, message, wParam, lParam);  }  /* @@ -112,7 +118,7 @@ void wStringSetValue(  	WORD len = (WORD)strlen( arg );  	SendMessage( b->hWnd, WM_SETTEXT, 0, (DWORD)arg );  #ifdef WIN32 -	SendMessage( b->hWnd, EM_SETSEL, len, len ); +	SendMessage( b->hWnd, EM_SETSEL, 0, -1 );  	SendMessage( b->hWnd, EM_SCROLLCARET, 0, 0L );  #else  	SendMessage( b->hWnd, EM_SETSEL, 0, MAKELPARAM(len,len) ); @@ -140,60 +146,92 @@ const char * wStringGetValue(  	return buff;  } +/** + * Get the string from a entry field. The returned pointer has to be free() after processing is complete. + *  + * \param bs IN string entry field + *  + * \return    pointer to entered string or NULL if entry field is empty. + */ -static void triggerString( -		wControl_p b ) +static char *getString(wString_p bs)  { -	wString_p bs = (wString_p)b; -	int cnt; +    char *tmpBuffer = NULL; +    UINT chars = SendMessage(bs->hWnd, EM_LINELENGTH, (WPARAM)0, 0L); -	if (bs->action) { -		*(WPARAM*)&mswTmpBuff[0] = 78; -		cnt = (int)SendMessage( bs->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); -		mswTmpBuff[cnt] = '\0'; -		if (bs->valueP) -			strcpy( bs->valueP, mswTmpBuff ); -		bs->action( mswTmpBuff, bs->data ); -		mswSetTrigger( NULL, NULL ); -	} +    if (chars) { +        tmpBuffer = malloc(chars > sizeof(WORD)? chars + 1 : sizeof(WORD) + 1); +        *(WORD *)tmpBuffer = chars; +        SendMessage(bs->hWnd, (UINT)EM_GETLINE, 0, (LPARAM)tmpBuffer); +        tmpBuffer[chars] = '\0'; +    } + +    return (tmpBuffer);  } +/** + * Retrieve and process string entry. If a string has been entered, the callback for + * the specific entry field is called. + * + * \param b IN string entry field + */ -LRESULT stringProc( -		wControl_p b, -		HWND hWnd, -		UINT message, -		WPARAM wParam, -		LPARAM lParam ) +static void triggerString( +    wControl_p b)  { -	wString_p bs = (wString_p)b; -	int cnt; -	int modified; - -	switch( message ) { - -	case WM_COMMAND: -		switch (WCMD_PARAM_NOTF) { -		case EN_KILLFOCUS: -			modified = (int)SendMessage( bs->hWnd, (UINT)EM_GETMODIFY, 0, 0L ); -			if (!modified) -				break; -			*(WPARAM*)&mswTmpBuff[0] = 78; -			cnt = (int)SendMessage( bs->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); -			mswTmpBuff[cnt] = '\0'; -			if (bs->valueP) -				strncpy( bs->valueP, mswTmpBuff, bs->valueL ); -			if (bs->action) { -				bs->action( mswTmpBuff, bs->data ); -				mswSetTrigger( NULL, NULL ); -			} -			break; -			SendMessage( bs->hWnd, (UINT)EM_SETMODIFY, FALSE, 0L ); -		} -		break; +    wString_p bs = (wString_p)b; + +    char *enteredString = getString(bs); +    if (enteredString) +    { +        if (bs->valueP) { +            strcpy(bs->valueP, enteredString); +        } +		if (bs->action) { +            bs->action(enteredString, bs->data); +        } +		free(enteredString);  	} +} -	return DefWindowProc( hWnd, message, wParam, lParam ); + +LRESULT stringProc( +    wControl_p b, +    HWND hWnd, +    UINT message, +    WPARAM wParam, +    LPARAM lParam) +{ +    wString_p bs = (wString_p)b; +    int modified; + +    switch (message) { + +    case WM_COMMAND: +        switch (WCMD_PARAM_NOTF) { +        case EN_KILLFOCUS: +            modified = (int)SendMessage(bs->hWnd, (UINT)EM_GETMODIFY, 0, 0L); +            if (!modified) { +                break; +            } + +            char *enteredString = getString(bs); +            if (enteredString) { +                if (bs->valueP) { +                    strcpy(bs->valueP, enteredString); +                } +                if (bs->action) { +                    bs->action(enteredString, bs->data); +                    mswSetTrigger(NULL, NULL); +                } +                free(enteredString); +            } +            SendMessage(bs->hWnd, (UINT)EM_SETMODIFY, FALSE, 0L); +        } +        break; +    } + +    return DefWindowProc(hWnd, message, wParam, lParam);  } @@ -249,10 +287,6 @@ wString_p wStringCreate(  		return b;  	} -#ifdef CONTROL3D -	Ctl3dSubclassCtl( b->hWnd); -#endif -  	newEditProc = MakeProcInstance( (XWNDPROC)pushEdit, mswHInst );  	oldEditProc = (XWNDPROC)GetWindowLong(b->hWnd, GWL_WNDPROC );  	SetWindowLong( b->hWnd, GWL_WNDPROC, (LONG)newEditProc ); diff --git a/app/wlib/mswlib/mswint.h b/app/wlib/mswlib/mswint.h index 2311415..e560053 100644 --- a/app/wlib/mswlib/mswint.h +++ b/app/wlib/mswlib/mswint.h @@ -1,6 +1,7 @@  #include "wlib.h"  #include "mswlib.h" -#include "dynarr.h" +//#include "dynarr.h" +#include "common.h"  #ifndef WIN32  /*#define CONTROL3D*/  #endif @@ -38,7 +39,9 @@  #define WSCROLL_PARAM_HWND	HIWORD(lParam)  #endif -#define CAST_AWAY_CONST (char *) +#ifndef CAST_AWAY_CONST +	#define CAST_AWAY_CONST (char *) +#endif  #define BOOL_T wBool_t  #define POS_T wPos_t @@ -122,16 +125,24 @@ struct wDraw_t {  		double DPI;  		wDrawRedrawCallBack_p drawRepaint;  		wDrawActionCallBack_p action; -		HBITMAP hBm; +		HBITMAP hBmMain; +		HBITMAP hBmTemp; +		HBITMAP hBmOld;  		HPEN hPen;  		HBRUSH hBrush;  		wDraw_p drawNext; -		HBITMAP hBmOld;  		wBool_t hasPalette;  		int paletteClock;  		HBITMAP hBmBackup;  		HDC hDcBackup;  		HBITMAP hBmBackupOld; +		void *background; +		wBool_t bTempMode; +		wBool_t bCopiedMain; + +		wPos_t lastX; +		wPos_t lastY; +  		};  extern HINSTANCE mswHInst; @@ -147,6 +158,7 @@ extern wDrawColor wDrawColorWhite;  extern wDrawColor wDrawColorBlack;  extern long mswThickFont;  extern double mswScale; +extern double scaleIcon;  DWORD mswGetBaseStyle( wWin_p );  char * mswStrdup( const char * ); @@ -190,4 +202,5 @@ void deleteBitmaps( void );  void mswDrawIcon( HDC, int, int, wIcon_p, int, COLORREF, COLORREF );  /* gwin32.c*/ -char *g_win32_getlocale (void);
\ No newline at end of file +char *g_win32_getlocale (void); + diff --git a/app/wlib/mswlib/mswlist.c b/app/wlib/mswlib/mswlist.c index 2453a5e..95ecec3 100644 --- a/app/wlib/mswlib/mswlist.c +++ b/app/wlib/mswlib/mswlist.c @@ -243,7 +243,7 @@ wBool_t wListSetValues(  		void * itemData )  {  	listData * ldp;		 -	WORD curSel; +	WORD curSel = -1;  	ldp = (listData*)malloc( sizeof *ldp );  	ldp->itemContext = itemData;  	ldp->bm = bm; diff --git a/app/wlib/mswlib/mswmenu.c b/app/wlib/mswlib/mswmenu.c index 815752a..d56e24d 100644 --- a/app/wlib/mswlib/mswmenu.c +++ b/app/wlib/mswlib/mswmenu.c @@ -31,6 +31,7 @@  #include <math.h>  #include <ctype.h>  #include <assert.h> +#include "misc.h"  #include "mswint.h"  #include "i18n.h" @@ -579,7 +580,7 @@ wMenuPush_p wMenuPushCreate(  {  	wMenuPush_p mi;  	int rc; -	char label[80]; +	char *label = malloc(strlen(labelStr) + 30 );	/**< The label and sufficient space for the keyboard shortcut */  	char *cp;  	char ac;  	UINT vk; @@ -591,9 +592,9 @@ wMenuPush_p wMenuPushCreate(  	mi->mparent = m;  	mi->acclKey = acclKey;  	mi->enabled = TRUE; -	strcpy( label, mi->labelStr ); +	strcpy(label, labelStr);  	modifier = 0; -	if ( acclKey != 0 ) { +	if ( acclKey != 0 && strlen(label ) < 60 ) {  		DYNARR_APPEND( acclTable_t, acclTable_da, 10 );  		cp = label + strlen( label );  		*cp++ = '\t'; @@ -625,6 +626,7 @@ wMenuPush_p wMenuPushCreate(  		acclTable(acclTable_da.cnt-1).mp = mi;  	}  	rc = AppendMenu( m->menu, MF_STRING, mi->index, label ); +	free(label);  	return mi;  } diff --git a/app/wlib/mswlib/mswmisc.c b/app/wlib/mswlib/mswmisc.c index e045cc8..6b5f1c9 100644 --- a/app/wlib/mswlib/mswmisc.c +++ b/app/wlib/mswlib/mswmisc.c @@ -22,6 +22,7 @@  #define _WIN32_WINNT 0x0500  #include <windows.h> +#include <shellapi.h>  #include <string.h>  #include <malloc.h>  #include <stdlib.h> @@ -30,8 +31,10 @@  #include <stdio.h>  #include <assert.h>  #include <htmlhelp.h> +#include "misc.h"  #include "mswint.h"  #include "i18n.h" +#include "FreeImage.h"  #if _MSC_VER > 1300  #define stricmp _stricmp @@ -47,6 +50,8 @@ char * mswStrdup(const char *);  #define ALARM_TIMER		(902)  #define BALLOONHELP_TIMER		(903)  #define TRIGGER_TIMER	(904) +#define CONTROLHILITEWIDTH (2) +#define CONTROLHILITECOLOR (RGB(0x3a,0x5f,0xcd))  #define WANT_LITTLE_LABEL_FONT @@ -78,6 +83,8 @@ HFONT mswLabelFont;  long mswThickFont = 1;  double mswScale = 1.0; +double scaleIcon = 1.0;				   /**< Scaling factor for toolbar icons */ +  callBacks_t *mswCallBacks[CALLBACK_CNT];  void closeBalloonHelp(void); @@ -87,7 +94,12 @@ static wControl_p getControlFromCursor(HWND, wWin_p *);   */  struct wWin_t { -    WOBJ_COMMON +	WOBJ_COMMON +	int validGeometry; +	int min_width; +	int max_width; +	int min_height; +	int max_height;      wPos_t lastX, lastY;      wPos_t padX, padY;      wControl_p first, last; @@ -174,7 +186,21 @@ static int dumpControls;  extern char *userLocale; - +// list of supported fileformats for image files +char * filterImageFiles[] = { N_("All image files"), +							"*.gif;*.jpg;*.jpeg;*.png;*.tif;*.tiff", +							N_("GIF files (*.gif)"), +							"*.gif", +							N_("JPEG files (*.jpeg,*.jpg)"), +							"*.jpg;*.jpeg", +							N_("PNG files (*.png)"), +							"*.png", +							N_("TIFF files (*.tiff, *.tif)"), +							"*.tif;*.tiff", +							N_("All files (*)"), +							"*", +							}; +  /*   *****************************************************************************   * @@ -610,6 +636,35 @@ static void getSavedSizeAndPos(  }  /** + * Set min and max dimensions for a window.  + * + * \param min_width IN minimum width of window + * \param max_width IN maximum width of window + * \param min_height IN minimum height of window + * \param max_height IN maximum height of window + * \param base_width IN unused on Windows + * \param base_height IN unused on Windows + * \param aspect_ration IN unused on Windows + */ +void wSetGeometry(wWin_p win, +	int min_width, +	int max_width, +	int min_height, +	int max_height, +	int base_width, +	int base_height, +	double aspect_ratio) +{ +	win->validGeometry = TRUE;	//remember that geometry was set +	win->min_width = min_width; +	win->max_width = max_width; +	win->min_height = min_height; +	win->max_height = max_height; + +	return; +} + +/**   * Create a window. Retrieves the saved size and position and restores the created window accordingly.   *   * \param hWnd IN parent window @@ -812,6 +867,10 @@ wWin_p wWinMainCreate(  	wPrefGetInteger("draw", "maximized", &maximize, 0L);  	option |= (maximize ? F_MAXIMIZE : 0); +	wPrefGetFloat(PREFSECTION, LARGEICON, &scaleIcon, 1.0); +	if (scaleIcon < 1.0) scaleIcon = 1.0; +	if (scaleIcon > 2.0) scaleIcon = 2.0; +      showCmd = SW_SHOW;      w = winCommonCreate(NULL, W_MAIN, option|F_RESIZE, "MswMainWindow",                          WS_OVERLAPPEDWINDOW, labelStr, winProc, x, y, data, @@ -819,13 +878,10 @@ wWin_p wWinMainCreate(      mswHWnd = w->hWnd;      if (!mswThickFont) { -        DWORD dw;          SendMessage(w->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, 0L);          hDc = GetDC(w->hWnd);          GetTextMetrics(hDc, &tm);          mswEditHeight = tm.tmHeight+2; -        dw = GetTextExtent(hDc, "AXqypj", 6); -        mswEditHeight = HIWORD(dw)+2;          ReleaseDC(w->hWnd, hDc);      } @@ -1380,12 +1436,11 @@ void wWinClear(  {  } -void wSetCursor( +void wSetCursor(wDraw_p win,      wCursor_t cursor)  {      switch (cursor) {      case wCursorNormal: -    case wCursorQuestion:      default:          SetCursor(LoadCursor(NULL, IDC_ARROW));          break; @@ -1401,6 +1456,42 @@ void wSetCursor(      case wCursorIBeam:          SetCursor(LoadCursor(NULL, IDC_IBEAM));          break; + +    case wCursorQuestion: +    	SetCursor(LoadCursor(NULL, IDC_HELP)); +    	break; + +    case wCursorHand: +       	SetCursor(LoadCursor(NULL, IDC_HAND)); +       	break; + +    case wCursorNo: +       	SetCursor(LoadCursor(NULL, IDC_NO)); +       	break; + +    case wCursorSizeAll: +       	SetCursor(LoadCursor(NULL, IDC_SIZEALL)); +       	break; + +    case wCursorSizeNESW: +       	SetCursor(LoadCursor(NULL, IDC_SIZENESW)); +       	break; + +    case wCursorSizeNWSE: +       	SetCursor(LoadCursor(NULL, IDC_SIZENWSE)); +       	break; + +    case wCursorSizeNS: +       	SetCursor(LoadCursor(NULL, IDC_SIZENS)); +       	break; + +    case wCursorSizeWE: +       	SetCursor(LoadCursor(NULL, IDC_SIZEWE)); +       	break; + +    case wCursorAppStart: +    	SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); +        break;      }      curCursor = cursor; @@ -1663,7 +1754,7 @@ void wControlSetLabel(      wControl_p b,      const char * labelStr)  { -    if (b->type == B_RADIO || b->type == B_TOGGLE) { +    if (b->type == B_RADIO ) {          ;      } else {          int lab_l; @@ -1693,8 +1784,6 @@ void wControlSetContext(      b->data = context;  } -static int controlHiliteWidth = 5; -static int controlHiliteWidth2 = 3;  void wControlHilite(      wControl_p b,      wBool_t hilite) @@ -1702,12 +1791,13 @@ void wControlHilite(      HDC hDc;      HPEN oldPen, newPen;      int oldMode; +	LOGBRUSH logBrush = { BS_SOLID, CONTROLHILITECOLOR, (ULONG_PTR)NULL };      if (b == NULL) {          return;      } -    if (!IsWindowVisible(b->parent->hWnd)) { +    if (!IsWindowVisible(b->parent->hWnd)) {	          return;      } @@ -1716,14 +1806,18 @@ void wControlHilite(      }      hDc = GetDC(b->parent->hWnd); -    newPen = CreatePen(PS_SOLID, controlHiliteWidth, RGB(0,0,0)); +	newPen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_BEVEL, +						  CONTROLHILITEWIDTH, +						  &logBrush, +						  0, +						  NULL);      oldPen = SelectObject(hDc, newPen);      oldMode = SetROP2(hDc, R2_NOTXORPEN); -    MoveTo(hDc, b->x-controlHiliteWidth2, b->y-controlHiliteWidth2); -    LineTo(hDc, b->x+b->w+controlHiliteWidth2, b->y-controlHiliteWidth2); -    LineTo(hDc, b->x+b->w+controlHiliteWidth2, b->y+b->h+controlHiliteWidth2); -    LineTo(hDc, b->x-controlHiliteWidth2, b->y+b->h+controlHiliteWidth2); -    LineTo(hDc, b->x-controlHiliteWidth2, b->y-controlHiliteWidth2); +	Rectangle(hDc, +		b->x - CONTROLHILITEWIDTH - 1, +		b->y - CONTROLHILITEWIDTH - 1, +		b->x + b->w + CONTROLHILITEWIDTH + 1, +		b->y + b->h + CONTROLHILITEWIDTH + 1);      SetROP2(hDc, oldMode);      SelectObject(hDc, oldPen);      DeleteObject(newPen); @@ -1766,6 +1860,26 @@ void wMessage(      ReleaseDC(w->hWnd, hDc);  } +/** + * Open a document using an external application + *  + * \param file + * \return TRUE on success, FALSE on error + *  + */ +unsigned wOpenFileExternal(char *file) +{ +	HINSTANCE res; + +	res = ShellExecute(mswHWnd, "open", file, NULL, NULL, SW_SHOW); + +	if ((int)res <= 32) { +		wNoticeEx(NT_ERROR, "Error when opening file!", "Cancel", NULL); +		return(FALSE); +	} + +	return(TRUE); +}  void wExit(int rc)  { @@ -2040,12 +2154,22 @@ int wNotice3(      }  } +/** + * Show help text for the given topic.  + * + * \param  topic The topic. if NULL the index page is shown. + */  void wHelp(      const char * topic)  {      char *pszHelpTopic;      HWND hwndHelp; +	char *theTopic = "index"; + +	if (topic) { +		theTopic = topic; +	}      if (!helpInitted) {          HtmlHelp(NULL, NULL, HH_INITIALIZE, (DWORD)&dwCookie) ; @@ -2054,9 +2178,9 @@ void wHelp(      /*	             "c:\\help.chm::/intro.htm>mainwin", */      /* attention: always adapt constant value (10) to needed number of formatting characters */ -    pszHelpTopic = malloc(strlen(helpFile) + strlen(topic) + 10); +    pszHelpTopic = malloc(strlen(helpFile) + strlen(theTopic) + 10);      assert(pszHelpTopic != NULL); -    sprintf(pszHelpTopic, "/%s.html", topic); +    sprintf(pszHelpTopic, "/%s.html", theTopic);      hwndHelp = HtmlHelp(mswHWnd, helpFile, HH_DISPLAY_TOPIC,                          (DWORD_PTR)pszHelpTopic); @@ -2068,6 +2192,8 @@ void wHelp(  } + +  void doHelpMenu(void * context)  {      HH_FTS_QUERY ftsQuery; @@ -2092,6 +2218,13 @@ void doHelpMenu(void * context)          HtmlHelp(mswHWnd, helpFile, HH_DISPLAY_SEARCH,(DWORD)&ftsQuery);          break; + +    case 3: /*Context*/ +    	const char * topic; +    	topic = GetCurCommandName(); +    	wHelp(topic); +    	break; +      default:          return;      } @@ -2099,11 +2232,16 @@ void doHelpMenu(void * context)      helpInitted = TRUE;  } +void wDoAccelHelp(wAccelKey_e key, void * context) { +	doHelpMenu(context); +} +  void wMenuAddHelp(      wMenu_p m)  { -    wMenuPushCreate(m, NULL, "&Contents", 0, doHelpMenu, (void*)1); -    wMenuPushCreate(m, NULL, "&Search for Help on...", 0, doHelpMenu, (void*)2); +    wMenuPushCreate(m, NULL, _("&Contents"), 0, doHelpMenu, (void*)1); +    wMenuPushCreate(m, NULL, _("&Search for Help on..."), 0, doHelpMenu, (void*)2); +    wMenuPushCreate(m, NULL, _("Co&mmand Context Help"), 0, doHelpMenu, (void*)3);  } @@ -2326,6 +2464,24 @@ struct wFilSel_t {  #define SELECTEDFILENAME_BUFFERSIZE	(8*1024)	/**<estimated size in case all param files are selected */ +char * +GetImageFileFormats(void) +{ +	char *filter = malloc(2048); +	char *current = filter; +	char *message; + +	for (int i = 0; i < sizeof(filterImageFiles) / sizeof(filterImageFiles[0]); i += 2) { +		message = gettext(filterImageFiles[i]); +		strcpy(current, message); +		current += strlen(message) + 1; +		strcpy(current, filterImageFiles[i + 1]); +		current += strlen(current) + 1; +	} +	*current = '\0'; +	return(filter); +} +  /**   * Run the file selector. After the selector is finished an array of filenames is   * created. Each filename will be fully qualified. The array and the number of @@ -2356,11 +2512,16 @@ int wFilSelect(              strcmp(dirName, ".") == 0) {          dirName = wGetUserHomeDir();      } -      memset(&ofn, 0, sizeof ofn);      ofn.lStructSize = sizeof ofn;      ofn.hwndOwner = mswHWnd; -    ofn.lpstrFilter = fs->extList; +	if (fs->option == FS_PICTURES) { +		ofn.lpstrFilter = GetImageFileFormats(); +	} +	else { +		ofn.lpstrFilter = fs->extList; +	} +      ofn.nFilterIndex = 0;      selFileName = malloc(SELECTEDFILENAME_BUFFERSIZE);      memset(selFileName, '\0', SELECTEDFILENAME_BUFFERSIZE); @@ -2599,6 +2760,23 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)      wAccelKey_e extChar;      switch (message) { +	case WM_GETMINMAXINFO: +		LPMINMAXINFO pMMI = (LPMINMAXINFO)lParam; +		inx = GetWindowWord(hWnd, 0); + +		if (inx >= CONTROL_BASE && inx <= controlMap_da.cnt) { +			w = (wWin_p)controlMap(inx - CONTROL_BASE).b; +			if (w != NULL) { +				if (w->validGeometry) { +					pMMI->ptMaxTrackSize.x = w->max_width; +					pMMI->ptMaxTrackSize.y = w->max_height; +					pMMI->ptMinTrackSize.x = w->min_width; +					pMMI->ptMinTrackSize.y = w->min_height; +				} +			} +		} +		return(0); +      case WM_MOUSEWHEEL:          inx = GetWindowWord(hWnd, 0);          b = getControlFromCursor(hWnd, NULL); @@ -2614,22 +2792,6 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)      case WM_DRAWITEM:      case WM_COMMAND:      case WM_MEASUREITEM: -    case WM_NOTVALID: -        if (WCMD_PARAM_ID == IDM_DOHELP) { -            b = getControlFromCursor(hWnd, NULL); -            closeBalloonHelp(); - -            if (!b) { -                return 0L; -            } - -            if (b->helpStr) { -                wHelp(b->helpStr); -            } - -            return 0L; -        } -          closeBalloonHelp();          if (WCMD_PARAM_ID < CONTROL_BASE || WCMD_PARAM_ID > (WPARAM)controlMap_da.cnt) { @@ -2913,26 +3075,26 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)      case WM_SETCURSOR:          /*if (any buttons down)          	break;*/ -        wSetCursor(curCursor); +        wSetCursor(NULL, curCursor);          if (!mswAllowBalloonHelp) { -            break; +            return TRUE;          }          if (IsIconic(mswHWnd)) { -            break; +            return TRUE;          }          b = getControlFromCursor(hWnd, NULL);          if (b == balloonControlButton) { -            break; +            return TRUE;          }          if (/*(!IsWindowEnabled(hWnd))*/ GetActiveWindow() != hWnd ||                                           (!b) || b->type == B_DRAW || b->helpStr == NULL) {              closeBalloonHelp(); -            break; +            return TRUE;          }          if (b != balloonHelpButton) { @@ -2940,19 +3102,19 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)          }          if (balloonHelpState != balloonHelpIdle) { -            break; +            return TRUE;          }          balloonHelpTimer = SetTimer(mswHWnd, BALLOONHELP_TIMER,                                      balloonHelpTimeOut, NULL);          if (balloonHelpTimer == (UINT)0) { -            break; +            return TRUE;          }          balloonHelpState = balloonHelpWait;          balloonHelpButton = b; -        break; +        return TRUE;      case WM_SYSCOMMAND:          inx = GetWindowWord(hWnd, 0); @@ -3211,13 +3373,13 @@ static BOOL InitApplication(HINSTANCE hinstCurrent)          return FALSE;      } -    wc.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; +    wc.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;      wc.lpfnWndProc = mswDrawPush;      wc.lpszClassName = mswDrawWindowClassName;      wc.cbWndExtra = 4;      if (!RegisterClass(&wc)) { -        mswFail("RegisterClass(drawClass)"); +		mswFail("RegisterClass(drawClass)");          return FALSE;      } @@ -3238,8 +3400,6 @@ int PASCAL WinMain(HINSTANCE hinstCurrent, HINSTANCE hinstPrevious,      HDC hDc;      char **argv;      int argc; -    TEXTMETRIC tm; -    DWORD dw;  	if (!hinstPrevious) {  		if (!InitApplication(hinstCurrent)) { @@ -3264,10 +3424,6 @@ int PASCAL WinMain(HINSTANCE hinstCurrent, HINSTANCE hinstPrevious,          mswScale = 1.0;      } -    GetTextMetrics(hDc, &tm); -    mswEditHeight = tm.tmHeight + 8; -    dw = GetTextExtent(hDc, "AXqypj", 6); -    mswEditHeight = HIWORD(dw)+2;      ReleaseDC(0, hDc);      mswCreateCheckBitmaps();      /* diff --git a/app/wlib/mswlib/mswmsg.c b/app/wlib/mswlib/mswmsg.c index 4a21921..6445299 100644 --- a/app/wlib/mswlib/mswmsg.c +++ b/app/wlib/mswlib/mswmsg.c @@ -42,6 +42,7 @@ static void repaintMessage(  	HFONT hFont;  	LOGFONT msgFont;  	double scale = 1.0; +	TEXTMETRIC textMetrics;  	hDc = GetDC( hWnd ); @@ -74,13 +75,15 @@ static void repaintMessage(  			hFont = SelectObject( hDc, mswLabelFont );  	} +	GetTextMetrics(hDc, &textMetrics); +  	rect.bottom = (long)(bm->y+( bm->h ));  	rect.right = (long)(bm->x+( scale * bm->w )); -	rect.top = bm->y; +	rect.top = bm->y+1;  	rect.left = bm->x;  	SetBkColor( hDc, GetSysColor( COLOR_BTNFACE ) ); -	ExtTextOut( hDc, bm->x, bm->y, ETO_CLIPPED|ETO_OPAQUE, &rect, bm->message, strlen( bm->message ), NULL ); +	ExtTextOut( hDc, bm->x, bm->y + ((bm->h + 2 - textMetrics.tmHeight) / 2), ETO_CLIPPED|ETO_OPAQUE, &rect, bm->message, strlen( bm->message ), NULL );  	if( scale != 1.0 )  		/* in case we did create a new font earlier, delete it now */ @@ -138,7 +141,7 @@ wPos_t wMessageGetHeight( long flags )  	if( flags & BM_SMALL )  		scale = SCALE_SMALL; -	return((wPos_t)((mswEditHeight - 4) * scale )); +	return((wPos_t)((mswEditHeight) * scale ));  #endif  } diff --git a/app/wlib/mswlib/mswpref.c b/app/wlib/mswlib/mswpref.c index eaa39fe..201171a 100644 --- a/app/wlib/mswlib/mswpref.c +++ b/app/wlib/mswlib/mswpref.c @@ -5,6 +5,7 @@  #include <commdlg.h>  #include <math.h>  #include <stdio.h> +#include "misc.h"  #include "mswint.h"  #include <shlobj.h>  #include <Shlwapi.h> diff --git a/app/wlib/mswlib/mswprint.c b/app/wlib/mswlib/mswprint.c index 91f05ea..13756c7 100644 --- a/app/wlib/mswlib/mswprint.c +++ b/app/wlib/mswlib/mswprint.c @@ -27,7 +27,7 @@ struct tagPD printDlg;  #endif  static int printStatus = FALSE;  static DOCINFO docInfo; -static double pageSizeW = 8.5, pageSizeH = 11.0; +static double tBorder = 0.0, rBorder = 0.0, bBorder = 0.0, lBorder = 0.0;  static double physSizeW = 8.5, physSizeH = 11.0;  static int pageCount = -1; @@ -66,10 +66,16 @@ void getPageDim( HDC hDc )  	size_h = GetDeviceCaps( hDc, VERTSIZE );  	print_d.w = res_w = GetDeviceCaps( hDc, HORZRES );  	print_d.h = res_h = GetDeviceCaps( hDc, VERTRES ); +	double pageSizeW, pageSizeH;  	pageSizeW = ((double)res_w)/print_d.wFactor;  	pageSizeH = ((double)res_h)/print_d.hFactor;  	physSizeW = ((double)dims.x)/print_d.wFactor;  	physSizeH = ((double)dims.y)/print_d.hFactor; +	// Get Borders/Margins - offs are the top, left borders +	lBorder = ((double)offs.x)/print_d.hFactor; +	tBorder = ((double)offs.y)/print_d.hFactor; +	rBorder = physSizeW-pageSizeW-lBorder; +	bBorder = physSizeH-pageSizeH-tBorder;  }  static wBool_t printInit( void ) @@ -83,6 +89,7 @@ static wBool_t printInit( void )  		return printerOk;  	}  	initted = TRUE; +	memset(&printDlg, 0, sizeof printDlg);  	printDlg.lStructSize = sizeof printDlg;  	printDlg.hwndOwner = NULL;  	printDlg.Flags = PD_RETURNDC|PD_RETURNDEFAULT; @@ -194,16 +201,36 @@ void wPrintSetup( wPrintSetupCallBack_p callback )  	}  } +const char* wPrintGetName() +{ +	static char sPrinterName[100]; +	HANDLE hDevNames = printDlg.hDevNames; +	DEVNAMES* pDevNames = GlobalLock(hDevNames); +	if (pDevNames == NULL) { +		strcpy(sPrinterName, "Printer"); +	} +	else { +		strncpy(sPrinterName, (char*)pDevNames + pDevNames->wDeviceOffset, sizeof sPrinterName - 1); +		sPrinterName[sizeof sPrinterName - 1] = '\0'; +	} +	GlobalUnlock( hDevNames ); +	return sPrinterName; +} -void wPrintGetPageSize( double *w, double *h ) +void wPrintGetMargins( +	double * tMargin, +	double * rMargin, +	double * bMargin, +	double * lMargin )  { -	printInit(); -	*w = pageSizeW; -	*h = pageSizeH; +	if ( tMargin ) *tMargin = tBorder; +	if ( rMargin ) *rMargin = rBorder; +	if ( bMargin ) *bMargin = bBorder; +	if ( lMargin ) *lMargin = lBorder;  } -void wPrintGetPhysSize( double *w, double *h ) +void wPrintGetPageSize( double *w, double *h )  {  	printInit();  	*w = physSizeW; @@ -378,10 +405,3 @@ wBool_t wPrintNewMargin( const char * name, double t, double b, double l, double  {  	return TRUE;  } - -void wPrintSetCallBacks( -		wAddPrinterCallBack_p newPrinter, -		wAddMarginCallBack_p newMargin, -		wAddFontAliasCallBack_p newFontAlias ) -{ -} diff --git a/app/wlib/mswlib/mswsplash.c b/app/wlib/mswlib/mswsplash.c index 47df6b7..172b563 100644 --- a/app/wlib/mswlib/mswsplash.c +++ b/app/wlib/mswlib/mswsplash.c @@ -204,8 +204,11 @@ wCreateSplash( char *appname, char *appver )  	/* create the title string */	  	pszBuf = malloc( strlen( appname ) + strlen( appver ) + 2 ); -	if( !pszBuf ) -		return( 0 ); +	if (!pszBuf) { +		GlobalUnlock(hgbl); +		GlobalFree(hgbl); +		return(0); +	}  	sprintf( pszBuf, "%s %s", appname, appver );  	lpw  += 1+MultiByteToWideChar (CP_ACP, 0, pszBuf, -1, (LPWSTR)lpw, 50); @@ -226,7 +229,6 @@ wCreateSplash( char *appname, char *appver )      GlobalUnlock(hgbl);       hSplash = CreateDialogIndirectParam( mswHInst, (LPDLGTEMPLATE) hgbl,           mswHWnd, (DLGPROC)SplashDlgProc, (LPARAM)hBmp );  -	GetLastError();  	/* free allocated memory */  	GlobalFree(hgbl);      diff --git a/app/wlib/mswlib/mswtext.c b/app/wlib/mswlib/mswtext.c index 293e2b4..0a0ce88 100644 --- a/app/wlib/mswlib/mswtext.c +++ b/app/wlib/mswlib/mswtext.c @@ -137,6 +137,9 @@ void wTextAppend(      if (b->option&BO_READONLY) {          SendMessage(b->hWnd, EM_SETREADONLY, 1, 0L);      } + +	// scroll to bottom of text box +	SendMessage(b->hWnd, EM_LINESCROLL, 0, 10000L);  } @@ -247,42 +250,54 @@ wBool_t wTextGetModified(      return (wBool_t)rc;  } +/** + * Get the size of the text in the text control including terminating '\0'. Note that + * the text actually might be shorter if the text includes CRs. + *  + * \param b IN text control + * \return required buffer size + */  int wTextGetSize(      wText_p b)  { -    int lc, l, len=0; -    lc = (int)SendMessage(b->hWnd, EM_GETLINECOUNT, 0, 0L); +	int len; -    for (l=0; l<lc ; l++) { -        int charIndex = (int)SendMessage(b->hWnd, EM_LINEINDEX, l, 0L); -        len += (int)SendMessage(b->hWnd, EM_LINELENGTH, charIndex, 0L) + 1; -    } - -    if (len == 1) { -        len = 0; -    } +	len = GetWindowTextLength(b->hWnd); -    return len; +    return len + 1;  } +/**  + * Get the text from a textentry. The buffer must be large enough for the text and + * the terminating \0. + * In case the string contains carriage returns these are removed. The returned string + * will be shortened accordingly.  + * To get the complete contents the buffer size must be equal or greater then the return + * value of wTextGetSize() + *  + * \param b IN text entry + * \param t IN/OUT buffer for text + * \param s IN size of buffer  + */ +   void wTextGetText(      wText_p b,      char * t,      int s)  { -    int lc, l, len; -    s--; -    lc = (int)SendMessage(b->hWnd, EM_GETLINECOUNT, 0, 0L); - -    for (l=0; l<lc && s>=0; l++) { -        *(WORD*)t = s; -        len = (int)SendMessage(b->hWnd, EM_GETLINE, l, (LPARAM)t); -        t += len; -        *t++ = '\n'; -        s -= len+1; -    } - -    *(t - 1) = '\0';		// overwrite the last \n added +	char *buffer = malloc(s); +	char *ptr = buffer; +	GetWindowText(b->hWnd, buffer, s); + +	// remove carriage returns +	while (*ptr) { +		if (*ptr != '\r') { +			*t = *ptr; +			t++; +		} +		ptr++; +	} +	free(buffer);  } diff --git a/app/wlib/mswlib/simple-gettext.c b/app/wlib/mswlib/simple-gettext.c index d213fc3..412eece 100644 --- a/app/wlib/mswlib/simple-gettext.c +++ b/app/wlib/mswlib/simple-gettext.c @@ -148,8 +148,9 @@ utf8_to_native( char *str, unsigned int len, int dummy )  		/* 2. convert from UTF-8 to system codepage */  		WideCharToMultiByte(CP_ACP, 0, (LPWSTR)buf, wcharLen, resBuffer, len + 1, NULL, NULL ); -		free( buf ); +  	} +	free(buf);  	return( resBuffer );	  } diff --git a/app/wlib/mswlib/unittest/CMakeLists.txt b/app/wlib/mswlib/unittest/CMakeLists.txt new file mode 100644 index 0000000..b91c1ff --- /dev/null +++ b/app/wlib/mswlib/unittest/CMakeLists.txt @@ -0,0 +1,11 @@ +# build unit tests for the xtrkcad Windows library + +add_executable(utf8test +  		utf8test.c +		../utf8conv.c +		) + +target_link_libraries(utf8test +  		 ${LIBS}) + +add_test(UTF8ConversionTest utf8test) diff --git a/app/wlib/mswlib/unittest/utf8test.c b/app/wlib/mswlib/unittest/utf8test.c new file mode 100644 index 0000000..5b00371 --- /dev/null +++ b/app/wlib/mswlib/unittest/utf8test.c @@ -0,0 +1,65 @@ +/** \file utf8test.c +* Unit tests for utf 8 conversion routines on Windows +*/ + +#include <setjmp.h> +#include <stdbool.h> +#include <string.h> + +#include <cmocka.h> + +#include <wlib.h> + +#define SIMPLEASCIITEXT "The quick brown fox jumps over the lazy dog." +#define UMLAUTTEXT "äöüÄÖÜß" + +static void +ASCIIText(void **state) +{ +	char output[100]; +	char result[100]; +	bool success; +	(void)state; + +	success = wSystemToUTF8(SIMPLEASCIITEXT, output, 100); +	assert_true((void *)success); + +	success = wUTF8ToSystem(output, result, 100); +	assert_true((void *)success); + +	assert_false(strcmp(SIMPLEASCIITEXT, result)); +} + +static void +Umlauts(void **state) +{ +	char output[100]; +	char result[100]; +	bool success; +	(void)state; + +	success = wIsUTF8(UMLAUTTEXT); +	assert_false((void *)success); + +	success = wSystemToUTF8(UMLAUTTEXT, output, 100); +	assert_true((void *)success); + +	success = wIsUTF8(output); +	assert_true((void *)success); + +	success = wUTF8ToSystem(output, result, 100); +	assert_true((void *)success); + +	assert_false(strcmp(UMLAUTTEXT, result)); +} + + +int main(void) +{ +    const struct CMUnitTest tests[] = { +		cmocka_unit_test(ASCIIText), +		cmocka_unit_test(Umlauts), +    }; + +    return cmocka_run_group_tests(tests, NULL, NULL); +}
\ No newline at end of file diff --git a/app/wlib/mswlib/utf8conv.c b/app/wlib/mswlib/utf8conv.c new file mode 100644 index 0000000..62ada76 --- /dev/null +++ b/app/wlib/mswlib/utf8conv.c @@ -0,0 +1,210 @@ +/** + * \file utf8conv.c. + * + * UTF-8 conversion functions + */ + +/*  XTrkCad - Model Railroad CAD + *  Copyright (C) 2020 Martin Fischer + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <malloc.h> +#include <stdbool.h> +#include <string.h> + +#include <Windows.h> + +#include <wlib.h> + +/** + * Convert system codepage to UTF 8 + * + * \param 		   inString		   The input string. + * \param [in,out] outString	   The output string buffer. + * \param 		   outStringLength Length of the output buffer + * + * \returns FALSE if it fails. + */ + +bool +wSystemToUTF8(const char *inString, char *outString, unsigned outStringLength) +{ +    unsigned int cnt = 2 * (strlen(inString) + 1); +    char *tempBuffer = malloc(cnt); + +    // convert to wide character (UTF16) +    MultiByteToWideChar(CP_ACP, +                        0, +                        inString, +                        -1, +                        (LPWSTR)tempBuffer, +                        cnt); + +    // convert from wide char to UTF-8 +    WideCharToMultiByte(CP_UTF8, +                        0, +                        (LPCWCH)tempBuffer, +                        -1, +                        (LPSTR)outString, +                        outStringLength, +                        NULL, +                        NULL); + +    free(tempBuffer); +    return true; +} + +/** + * Convert from UTF-8 to system codepage + * + * \param 		   inString		   The input string. + * \param [in,out] outString	   the output string. + * \param 		   outStringLength Length of the output buffer. + * + * \returns True if it succeeds, false if it fails. + */ + +bool +wUTF8ToSystem(const char *inString, char *outString, unsigned outStringLength) +{ +    unsigned int cnt = 2 * (strlen(inString) + 1); +    char *tempBuffer = malloc(cnt); + +    // convert to wide character (UTF16) +    MultiByteToWideChar(CP_UTF8, +                        0, +                        inString, +                        -1, +                        (LPWSTR)tempBuffer, +                        cnt); + + +    cnt = WideCharToMultiByte(CP_ACP, +                              0, +                              (LPCWCH)tempBuffer, +                              -1, +                              (LPSTR)outString, +                              0L, +                              NULL, +                              NULL); + +    if (outStringLength <= cnt) { +        return (false); +    } + +    // convert from wide char to system codepage +    WideCharToMultiByte(CP_ACP, +                        0, +                        (LPCWCH)tempBuffer, +                        -1, +                        (LPSTR)outString, +                        outStringLength, +                        NULL, +                        NULL); + +    free(tempBuffer); +    return true; +} + +/** + * Is passed string in correct UTF-8 format? + * Taken from https://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c + * + * \param  string The string to check. + * + * \returns True if UTF 8, false if not. + */ + +bool wIsUTF8(const char * string) +{ +    if (!string) { +        return 0; +    } + +    const unsigned char * bytes = (const unsigned char *)string; +    while (*bytes) { +        if ((// ASCII +                    // use bytes[0] <= 0x7F to allow ASCII control characters +                    bytes[0] == 0x09 || +                    bytes[0] == 0x0A || +                    bytes[0] == 0x0D || +                    (0x20 <= bytes[0] && bytes[0] <= 0x7E) +                ) +           ) { +            bytes += 1; +            continue; +        } + +        if ((// non-overlong 2-byte +                    (0xC2 <= bytes[0] && bytes[0] <= 0xDF) && +                    (0x80 <= bytes[1] && bytes[1] <= 0xBF) +                ) +           ) { +            bytes += 2; +            continue; +        } + +        if ((// excluding overlongs +                    bytes[0] == 0xE0 && +                    (0xA0 <= bytes[1] && bytes[1] <= 0xBF) && +                    (0x80 <= bytes[2] && bytes[2] <= 0xBF) +                ) || +                (// straight 3-byte +                    ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) || +                     bytes[0] == 0xEE || +                     bytes[0] == 0xEF) && +                    (0x80 <= bytes[1] && bytes[1] <= 0xBF) && +                    (0x80 <= bytes[2] && bytes[2] <= 0xBF) +                ) || +                (// excluding surrogates +                    bytes[0] == 0xED && +                    (0x80 <= bytes[1] && bytes[1] <= 0x9F) && +                    (0x80 <= bytes[2] && bytes[2] <= 0xBF) +                ) +           ) { +            bytes += 3; +            continue; +        } + +        if ((// planes 1-3 +                    bytes[0] == 0xF0 && +                    (0x90 <= bytes[1] && bytes[1] <= 0xBF) && +                    (0x80 <= bytes[2] && bytes[2] <= 0xBF) && +                    (0x80 <= bytes[3] && bytes[3] <= 0xBF) +                ) || +                (// planes 4-15 +                    (0xF1 <= bytes[0] && bytes[0] <= 0xF3) && +                    (0x80 <= bytes[1] && bytes[1] <= 0xBF) && +                    (0x80 <= bytes[2] && bytes[2] <= 0xBF) && +                    (0x80 <= bytes[3] && bytes[3] <= 0xBF) +                ) || +                (// plane 16 +                    bytes[0] == 0xF4 && +                    (0x80 <= bytes[1] && bytes[1] <= 0x8F) && +                    (0x80 <= bytes[2] && bytes[2] <= 0xBF) && +                    (0x80 <= bytes[3] && bytes[3] <= 0xBF) +                ) +           ) { +            bytes += 4; +            continue; +        } + +        return false; +    } + +    return true; +}
\ No newline at end of file | 
