/** \file util.c * Some basic window functions */ /* XTrkCad - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #define GTK_DISABLE_SINGLE_INCLUDES #define GDK_DISABLE_DEPRECATED #define GTK_DISABLE_DEPRECATED #define GSEAL_ENABLE #include #include #include #include "gtkint.h" #include "i18n.h" extern wWin_p gtkMainW; long debugWindow = 0; char wConfigName[ 256 ]; const char * wNames[] = { "MAIN", "POPUP", "BUTT", "CANCEL", "POPUP", "TEXT", "INTEGER", "FLOAT", "LIST", "DROPLIST", "COMBOLIST", "RADIO", "TOGGLE", "DRAW", "MENU" "MULTITEXT", "MESSAGE", "LINES", "MENUITEM", "BOX" }; #if 0 static wBool_t reverseIcon = #if defined(linux) FALSE; #else TRUE; #endif #endif /* ***************************************************************************** * * Internal Utility functions * ***************************************************************************** */ /** * Create a pixbuf from a two colored bitmap in XBM format. * * \param ip the XBM data * \returns the pixbuf */ GdkPixbuf* wlibPixbufFromXBM( wIcon_p ip) { GdkPixbuf * pixbuf; char line0[40]; char line2[40]; char ** pixmapData; int row, col, wb; long rgb; const char * bits; wb = (ip->w + 7) / 8; pixmapData = (char**) malloc((3 + ip->h) * sizeof *pixmapData); pixmapData[0] = line0; rgb = wDrawGetRGB(ip->color); sprintf(line0, " %ld %ld 2 1", ip->w, ip->h); sprintf(line2, "# c #%2.2lx%2.2lx%2.2lx", (rgb >> 16)&0xFF, (rgb >> 8)&0xFF, rgb & 0xFF); pixmapData[1] = ". c None s None"; pixmapData[2] = line2; bits = ip->bits; for (row = 0; row < ip->h; row++) { pixmapData[row + 3] = (char*) malloc((ip->w + 1) * sizeof **pixmapData); for (col = 0; col < ip->w; col++) { if (bits[ row * wb + (col >> 3) ] & (1 << (col & 07))) { pixmapData[row + 3][col] = '#'; } else { pixmapData[row + 3][col] = '.'; } } pixmapData[row + 3][ip->w] = 0; } pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) pixmapData); for (row = 0; row < ip->h; row++) { free(pixmapData[row + 3]); } free(pixmapData); return pixbuf; } /** * Add a label to an existing widget * * \param b IN widget * \param labelStr IN label to add * \returns size of label */ int wlibAddLabel(wControl_p b, const char * labelStr) { GtkRequisition requisition, reqwidget; if (labelStr == NULL) { return 0; } b->label = gtk_label_new(wlibConvertInput(labelStr)); gtk_widget_size_request(b->label, &requisition); if (b->widget) { gtk_widget_size_request(b->widget, &reqwidget); } else { reqwidget.height = requisition.height; } gtk_container_add(GTK_CONTAINER(b->parent->widget), b->label); gtk_fixed_move(GTK_FIXED(b->parent->widget), b->label, b->realX - requisition.width - 8, b->realY + (reqwidget.height/2 - requisition.height/2)); gtk_widget_show(b->label); return requisition.width + 8; } /** * Allocate and initialize the data structure for a new widget * * \param parent IN parent window * \param type IN type of new widget * \param origX IN x position * \param origY IN y position * \param labelStr IN text label * \param size IN size * \param data IN user data to keep with widget * \returns */ void * wlibAlloc( wWin_p parent, wType_e type, wWinPix_t origX, wWinPix_t origY, const char * labelStr, int size, void * data) { wControl_p w = (wControl_p) malloc(size); char * cp; memset(w, 0, size); if (w == NULL) { abort(); } w->outline = FALSE; w->type = type; w->parent = parent; w->origX = origX; w->origY = origY; if (labelStr) { cp = (char*) malloc(strlen(labelStr) + 1); w->labelStr = cp; for (; *labelStr; labelStr++) if (*labelStr != '&') { *cp++ = *labelStr; } *cp = 0; } w->doneProc = NULL; w->data = data; return w; } /** * Calculate the position for a widget * * \param b IN widget */ void wlibComputePos( wControl_p b) { wWin_p w = b->parent; if (b->origX >= 0) { b->realX = b->origX; } else { b->realX = w->lastX + (-b->origX) - 1; } if (b->origY >= 0) { b->realY = b->origY + BORDERSIZE + ((w->option & F_MENUBAR) ? w->menu_height : 0); } else { b->realY = w->lastY + (-b->origY) - 1; } } /** * Initialize the internal structure with the size of the widget * * \param b IN widget */ void wlibControlGetSize( wControl_p b) { GtkRequisition requisition; gtk_widget_size_request(b->widget, &requisition); b->w = requisition.width; b->h = requisition.height; } /** * ??? * \param b IN widget */ void wlibAddButton( wControl_p b) { wWin_p win = b->parent; wBool_t resize = FALSE; if (win->first == NULL) { win->first = b; } else { win->last->next = b; } win->last = b; b->next = NULL; b->parent = win; win->lastX = b->realX + b->w; win->lastY = b->realY + b->h; if (win->option & F_AUTOSIZE) { if (win->lastX > win->realX) { win->realX = win->lastX; if (win->w != (win->realX + win->origX)) { resize = TRUE; win->w = (win->realX + win->origX); } } if (win->lastY > win->realY) { win->realY = win->lastY; if (win->h != (win->realY + win->origY)) { resize = TRUE; win->h = (win->realY + win->origY); } } if (win->shown) { if (resize) { gtk_widget_set_size_request(win->gtkwin, win->w, win->h); gtk_widget_set_size_request(win->widget, win->w, win->h); } } } } /** * Find the widget at a position * \param win IN searched widget's parent window * \param x IN x position inside parent * \param y IN y position inside parent * \returns the widget, NULL if none at the position */ wControl_p wlibGetControlFromPos( wWin_p win, wWinPix_t x, wWinPix_t y) { wControl_p b; wWinPix_t xx, yy; for (b = win->first; b != NULL; b = b->next) { if (b->widget && gtk_widget_get_visible(b->widget)) { xx = b->realX; yy = b->realY; if (xx <= x && x < xx + b->w && yy <= y && y < yy + b->h) { return b; } } } return NULL; } /* ***************************************************************************** * * Exported Utility Functions * ***************************************************************************** */ /** * Beep! * \return */ void wBeep(void) { gdk_display_beep(gdk_display_get_default()); } /** * Flushs all commands to the Window. */ void wFlush( void) { while (gtk_events_pending()) { gtk_main_iteration_do(FALSE); } gdk_display_sync(gdk_display_get_default()); } /** * Not implemented * \returns */ void wWinTop(wWin_p win) { } extern long dontHideCursor; /** * Set the cursor in GTK * * \param cursor IN */ 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 ((cursor == wCursorNone) && dontHideCursor) { return; } //Ignore if we dont want to suppress 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); break; 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); } /** * Not implemented * \returns */ const char * wMemStats(void) { return "No stats available"; } /** * Get the size of the screen * * \param w IN pointer to width * \param h IN pointer to height */ void wGetDisplaySize(wWinPix_t * w, wWinPix_t * h) { 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; /** * Convert a string to UTF-8 * * \param inString IN string to convert * \returns pointer to converted string, valid until next call to conversion function */ char * wlibConvertInput(const char * inString) { const char * cp; char * cq; int extCharCnt, inCharCnt; /* Already UTF-8 encoded? */ if (g_utf8_validate(inString, -1, NULL)) /* Yes, do not double-convert */ { return (char*) inString; } for (cp = inString, extCharCnt = 0; *cp; cp++) { if (((*cp)&0x80) != '\0') { extCharCnt++; } } inCharCnt = cp - inString; if (extCharCnt == '\0') { return (char*) inString; } DYNARR_SET(char, conversionBuffer_da, inCharCnt + extCharCnt + 1); for (cp = inString, cq = (char*) conversionBuffer_da.ptr; *cp; cp++) { if (((*cp)&0x80) != 0) { *cq++ = 0xC0 + (((*cp)&0xC0) >> 6); *cq++ = 0x80 + ((*cp)&0x3F); } else { *cq++ = *cp; } } *cq = 0; return (char*) conversionBuffer_da.ptr; } /** * Convert a string from UTF-8 to system codepage * * \param inString IN string to convert * \returns pointer to converted string, valid until next call to conversion function */ char * wlibConvertOutput(const char * inString) { const char * cp; char * cq; int extCharCnt, inCharCnt; for (cp = inString, extCharCnt = 0; *cp; cp++) { if (((*cp)&0xC0) == 0x80) { extCharCnt++; } } inCharCnt = cp - inString; if (extCharCnt == '\0') { return (char*) inString; } DYNARR_SET(char, conversionBuffer_da, inCharCnt + 1); for (cp = inString, cq = (char*) conversionBuffer_da.ptr; *cp; cp++) { if (((*cp)&0x80) != 0) { *cq++ = 0xC0 + (((*cp)&0xC0) >> 6); *cq++ = 0x80 + ((*cp)&0x3F); } else { *cq++ = *cp; } } *cq = '\0'; return (char*) conversionBuffer_da.ptr; } /*-----------------------------------------------------------------*/ static dynArr_t accelData_da; #define accelData(N) DYNARR_N( accelData_t, accelData_da, N ) static guint accelKeyMap[] = { 0, /* wAccelKey_None, */ GDK_KEY_Delete, /* wAccelKey_Del, */ GDK_KEY_Insert, /* wAccelKey_Ins, */ GDK_KEY_Home, /* wAccelKey_Home, */ GDK_KEY_End, /* wAccelKey_End, */ GDK_KEY_Page_Up, /* wAccelKey_Pgup, */ GDK_KEY_Page_Down, /* wAccelKey_Pgdn, */ GDK_KEY_Up, /* wAccelKey_Up, */ GDK_KEY_Down, /* wAccelKey_Down, */ GDK_KEY_Right, /* wAccelKey_Right, */ GDK_KEY_Left, /* wAccelKey_Left, */ GDK_KEY_BackSpace, /* wAccelKey_Back, */ GDK_KEY_F1, /* wAccelKey_F1, */ GDK_KEY_F2, /* wAccelKey_F2, */ GDK_KEY_F3, /* wAccelKey_F3, */ GDK_KEY_F4, /* wAccelKey_F4, */ GDK_KEY_F5, /* wAccelKey_F5, */ GDK_KEY_F6, /* wAccelKey_F6, */ GDK_KEY_F7, /* wAccelKey_F7, */ GDK_KEY_F8, /* wAccelKey_F8, */ GDK_KEY_F9, /* wAccelKey_F9, */ GDK_KEY_F10, /* wAccelKey_F10, */ GDK_KEY_F11, /* wAccelKey_F11, */ GDK_KEY_F12, /* wAccelKey_F12, */ GDK_KEY_KP_Add, /* wAccelKey_Numpad_Add */ GDK_KEY_KP_Subtract /* wAccelKey_Numpad_Subtract */ }; /** * Create an accelerator key * * \param key IN primary key stroke * \param modifier IN modifier (shift, ctrl, etc.) * \param action IN function to call * \param data IN data to pass to function */ void wAttachAccelKey( wAccelKey_e key, int modifier, wAccelKeyCallBack_p action, void * data) { accelData_t * ad; // if (key < 1 || key > wAccelKey_F12) { // fprintf(stderr, "wAttachAccelKey(%d) out of range\n", (int) key); // return; // } DYNARR_APPEND(accelData_t, accelData_da, 10); ad = &accelData(accelData_da.cnt - 1); ad->key = key; ad->modifier = modifier; ad->action = action; ad->data = data; } /** * Check for accelerator definition a pressed key * * \param event IN key press event * \returns pointer to accel key structure, NULL if not existing */ struct accelData_t * wlibFindAccelKey( GdkEventKey * event) { accelData_t * ad; int modifier = 0; if ((event->state & GDK_SHIFT_MASK)) { modifier |= WKEY_SHIFT; } if ((event->state & GDK_CONTROL_MASK)) { modifier |= WKEY_CTRL; } if ((event->state & GDK_MOD1_MASK)) { modifier |= WKEY_ALT; } for (ad = &accelData(0); ad<&accelData(accelData_da.cnt); ad++) if (event->keyval == accelKeyMap[ad->key] && modifier == ad->modifier) { return ad; } return NULL; } /** * Perform action when an accelerator key was pressed * * \param event IN key press event * \returns TRUE if valid accelerator, FALSE if not */ wBool_t wlibHandleAccelKey( GdkEventKey *event) { accelData_t * ad = wlibFindAccelKey(event); if (ad) { ad->action(ad->key, ad->data); return TRUE; } return FALSE; } /** * Add control to circular list of synonymous controls. Synonymous controls are kept in sync by * calling wControlLinkedActive for one member of the list * \todo This is similar to the concept of action in gtk/glib \ * Maybe this would be easier to use * * \param b1 IN first control * \param b2 IN second control * \return none */ void wControlLinkedSet(wControl_p b1, wControl_p b2) { b2->synonym = b1->synonym; if (b2->synonym == NULL) { b2->synonym = b1; } b1->synonym = b2; } /** * Activate/deactivate a group of synonymous controls. * * \param b IN control * \param active IN state * \return none */ void wControlLinkedActive(wControl_p b, int active) { wControl_p savePtr = b; if (savePtr->type == B_MENUITEM) { wMenuPushEnable((wMenuPush_p) savePtr, active); } else { wControlActive(savePtr, active); } savePtr = savePtr->synonym; while (savePtr && savePtr != b) { if (savePtr->type == B_MENUITEM) { wMenuPushEnable((wMenuPush_p) savePtr, active); } else { wControlActive(savePtr, active); } savePtr = savePtr->synonym; } }