diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-08-08 11:53:00 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-08-08 11:53:00 +0200 |
commit | b623f5953691b2a0614e6f1f4def86bdbb9a4113 (patch) | |
tree | 18102bd36f7e22eb2ba2b9f880e4cb29346f4cb8 /app/wlib | |
parent | 359b557176b9bb2ff1aed2082641eed39c358d0d (diff) |
New upstream version 5.2.0Beta2.1upstream/5.2.0Beta2.1
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 |