# HG changeset patch # User ib # Date 1397345369 0 # Node ID 84c93a60ead3acb650302828a6844f609656e3aa # Parent 2ef6693131f7e9088a110b5717d10c9dc311a441 Add new item 'rpotmeter'. This is the missing counterpart to hpotmeter and vpotmeter allowing rotary control elements in a GUI skin now. Based on an idea and a realization by Hans-Dieter Kosch, hdkosch kabelbw de. Additionally, update (and revise) documentation. diff -r 2ef6693131f7 -r 84c93a60ead3 Changelog --- a/Changelog Fri Apr 11 09:34:31 2014 +0000 +++ b/Changelog Sat Apr 12 23:29:29 2014 +0000 @@ -40,7 +40,7 @@ * Console message with information on deprecated (but still supported) entries in the skin configuration file * New symbol character (r) and new dynamic label variables ($D, $U, $P) - * New item: pimage + * New items (pimage, rpotmeter) 1.1: "We gave up on 1.0" diff -r 2ef6693131f7 -r 84c93a60ead3 DOCS/xml/de/skin.xml --- a/DOCS/xml/de/skin.xml Fri Apr 11 09:34:31 2014 +0000 +++ b/DOCS/xml/de/skin.xml Sat Apr 12 23:29:29 2014 +0000 @@ -444,15 +444,28 @@ + + + + + + + + rpotmeter = button, bwidth, bheight, phases, numphases, x0, y0, x1, y1, default, X, Y, width, height, message + + + - Platziere einen horizontal (hpotmeter) oder vertikal (vpotmeter) Potentiometer mit + Platziere ein horizontales (hpotmeter), vertikales (vpotmeter) oder drehbares (rpotmeter) Potentiometer der Größe width * height an Position X,Y. Die Grafik kann in unterschiedliche Teile für die verschiedenen Phasen des Potentiometers aufgeteilt werden (du kannst zum Beispiel eines für die Lautstärkeregelung haben, das von rot nach grün wechselt, während sich sein Wert vom Minimum zum Maximum ändert.). - hpotmeter kann einen Button besitzen, der horizontal gezogen - werden kann. Die Parameter sind: + Alle Potentiometer können einen Button besitzen, der bei einem + hpotmeter und vpotmeter + gezogen werden kann. Ein rpotmeter kann auch + ohne Button gedreht werden. Die Parameter sind: @@ -471,9 +484,11 @@ phases - die für die verschiedenen Phasen - zu verwendende Grafik des hpotmeter. Ein spezieller Wert von NULL + zu verwendende Grafik des Potentiometers. Ein spezieller Wert von NULL kann benutzt werden, wenn du keine solche Grafik anwenden willst. Die Grafik muss - in numphases untereinander (bzw. für vpotmeter nebeneinander) liegende Teile wie folgt aufgeteilt werden: + in numphases untereinander (bzw. für + vpotmeter nebeneinander) liegende Teile + wie folgt aufgeteilt werden: @@ -497,25 +512,44 @@ - default - Standardwert für hpotmeter + x0, + y0 und + x1, + y1 - Position des + 0%-Start-Punkts und 100%-Stopp-Punkts des Potentiometers + (nur für rpotmeter) + + + Die erste Koordinate x0,y0 + definiert den 0%-Start-Punkt (auf dem Rand des + Potentiometer) in der Grafik für Phase #1 und die zweite + Koordinate x1,y1 + den 100%-Stopp-Punkt in der Grafik für Phase #n - mit + anderen Worten: die Koordinaten der Spitze der Markierung + auf dem Potentiometer in den beiden einzelnen Grafiken. + + + + + default - Voreinstellung des Potentiometers (im Bereich 0 bis 100) - X, Y - Position fürs hpotmeter + X, Y - Position des Potentiometers width, height - Breite und Höhe - des hpotmeter + des Potentiometers message - die Nachricht, die erzeugt werden soll, - wenn der Wert des hpotmeter geändert wird + wenn der Wert des Potentiometers geändert wird diff -r 2ef6693131f7 -r 84c93a60ead3 DOCS/xml/en/skin.xml --- a/DOCS/xml/en/skin.xml Fri Apr 11 09:34:31 2014 +0000 +++ b/DOCS/xml/en/skin.xml Sat Apr 12 23:29:29 2014 +0000 @@ -377,13 +377,23 @@ vpotmeter = button, bwidth, bheight, phases, numphases, default, X, Y, width, height, message - Place a horizontal (hpotmeter) or vertical (vpotmeter) potmeter of + + + + + + rpotmeter = button, bwidth, bheight, phases, numphases, x0, y0, x1, y1, default, X, Y, width, height, message + + + Place a horizontal (hpotmeter), vertical (vpotmeter) or rotary (rpotmeter) potmeter of width * height size at position X,Y. The image can be divided into different parts for the different phases of the potmeter (for example, you can have a pot for volume control that turns from green to red while its value changes from the minimum - to the maximum.). hpotmeter can have a button that can be - dragged horizontally. The parameters are: + to the maximum). All potentiometers can have a button that can be dragged + with a hpotmeter and vpotmeter. A + rpotmeter can be spun even without a button. The + parameters are: button - the image to be used for the @@ -397,9 +407,10 @@ phases - the image to be used for the - different phases of the hpotmeter. A special value of NULL + different phases of the potentiometer. A special value of NULL can be used if you want no such image. The image must be divided into - numphases parts below each other (resp. side by side for vpotmeter) like this: + numphases parts below each other (resp. side by side + for vpotmeter) like this: +------------+ | phase #1 | vpotmeter only: @@ -417,19 +428,31 @@ phases image - default - default value for hpotmeter + x0, + y0 and + x1, + y1 - position of the 0% start + point and 100% stop point for the potentiometer (rpotmeter only) + The first coordinate x0,y0 + defines the 0% start point (on the edge of the potentiometer) in the + image for phase #1 and the second coordinate x1,y1 + the 100% stop point in the image for phase #n - in other words, the + coordinates of the tip of the mark on the potentiometer in the two + individual images. + + default - default value for the potentiometer (in the range 0 to 100) - X, Y - position for the hpotmeter + X, Y - position for the potentiometer width, height - width and height - of the hpotmeter + of the potentiometer message - the message to be generated when the - value of hpotmeter is changed + value of the potentiometer is changed diff -r 2ef6693131f7 -r 84c93a60ead3 gui/app/app.c --- a/gui/app/app.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/app/app.c Sat Apr 12 23:29:29 2014 +0000 @@ -21,6 +21,8 @@ * @brief GUI application helpers */ +#include + #include "app.h" #include "gui.h" #include "gui/skin/font.h" @@ -180,6 +182,31 @@ } /** + * @brief Calculate the radian of a point inside the visual representation + * of an item. + * + * @param item pointer to the item + * @param x x position of the point + * @param y y position of the point + * + * @return radian of the point + * + * @note The return value is a @a clockwise radian. + */ +double appRadian(guiItem *item, int x, int y) +{ + double tx, ty; + + // transform the center to (0,0) + tx = x - item->width / 2.0; + ty = y - item->height / 2.0; + + // the y-axis is upside down and must be mirrored + // the x-axis is being mirrored for a clockwise radian + return (tx == 0.0 && ty == 0.0 ? 0.0 : atan2(-ty, -tx) + M_PI); +} + +/** * @brief Modify the value of the item belonging to an event. * * @param event event diff -r 2ef6693131f7 -r 84c93a60ead3 gui/app/app.h --- a/gui/app/app.h Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/app/app.h Sat Apr 12 23:29:29 2014 +0000 @@ -102,6 +102,7 @@ itDLabel, itHPotmeter, itVPotmeter, + itRPotmeter, itPimage, itMenu, itPLMButton = 100, @@ -132,6 +133,7 @@ int pbwidth, pbheight; int numphases; float value; + double zeropoint, arclength; int message; @@ -180,6 +182,7 @@ guiItem *appFindItem(int event); int appFindMessage(const char *name); void appFreeStruct(void); +double appRadian(guiItem *item, int x, int y); void btnModify(int event, float value); void btnSet(int event, int state); void btnValue(int event, float *value); diff -r 2ef6693131f7 -r 84c93a60ead3 gui/app/gui.h --- a/gui/app/gui.h Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/app/gui.h Sat Apr 12 23:29:29 2014 +0000 @@ -48,9 +48,9 @@ #define isInside(x, y, tx, ty, bx, by) ((x) > (tx) && (y) > (ty) && (x) < (bx) && (y) < (by)) /// Check whether #guiItem @a item has a button (and thus a pressed state). -#define hasButton(item) (item.type == itButton || item.type == itHPotmeter || item.type == itVPotmeter) +#define hasButton(item) (item.type == itButton || item.type == itHPotmeter || item.type == itVPotmeter || item.type == itRPotmeter) /// Check whether #guiItem @a item utilizes member 'value' -#define hasValue(item) (item.type == itHPotmeter || item.type == itVPotmeter || item.type == itPimage) +#define hasValue(item) (item.type == itHPotmeter || item.type == itVPotmeter || item.type == itRPotmeter || item.type == itPimage) #endif /* MPLAYER_GUI_GUI_H */ diff -r 2ef6693131f7 -r 84c93a60ead3 gui/skin/skin.c --- a/gui/skin/skin.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/skin/skin.c Sat Apr 12 23:29:29 2014 +0000 @@ -21,6 +21,7 @@ * @brief Skin parser */ +#include #include #include @@ -35,6 +36,7 @@ #include "help_mp.h" #include "mp_msg.h" +#include "libavutil/attributes.h" #include "libavutil/avstring.h" #include "libavutil/common.h" @@ -589,13 +591,15 @@ } /** - * @brief Parse a hpotmeter or vpotmeter definition. + * @brief Parse a hpotmeter, vpotmeter or rpotmeter definition. * - * Parameters: button,bwidth,bheight,phases,numphases,default,x,y,width,height,message + * Parameters: button,bwidth,bheight,phases,numphases,[x0,y0,x1,y1,]default,x,y,width,height,message * * @param item pointer to item to store the parameters in * @param in definition to be analyzed * + * @note item->type is already available. + * * @return 0 (ok) or 1 (error) */ static int parse_potmeter(guiItem *item, char *in) @@ -603,6 +607,7 @@ unsigned char bfname[256]; unsigned char phfname[256]; unsigned char buf[512]; + int i = 0, av_uninit(x0), av_uninit(y0), av_uninit(x1), av_uninit(y1); int bwidth, bheight, num, d, x, y, w, h, message; if (!window_item(currItem)) @@ -613,17 +618,25 @@ if (in_window("menu")) return 1; - cutStr(in, bfname, ',', 0); - bwidth = cutInt(in, ',', 1); - bheight = cutInt(in, ',', 2); - cutStr(in, phfname, ',', 3); - num = cutInt(in, ',', 4); - d = cutInt(in, ',', 5); - x = cutInt(in, ',', 6); - y = cutInt(in, ',', 7); - w = cutInt(in, ',', 8); - h = cutInt(in, ',', 9); - cutStr(in, buf, ',', 10); + cutStr(in, bfname, ',', i++); + bwidth = cutInt(in, ',', i++); + bheight = cutInt(in, ',', i++); + cutStr(in, phfname, ',', i++); + num = cutInt(in, ',', i++); + + if (item->type == itRPotmeter) { + x0 = cutInt(in, ',', i++); + y0 = cutInt(in, ',', i++); + x1 = cutInt(in, ',', i++); + y1 = cutInt(in, ',', i++); + } + + d = cutInt(in, ',', i++); + x = cutInt(in, ',', i++); + y = cutInt(in, ',', i++); + w = cutInt(in, ',', i++); + h = cutInt(in, ',', i++); + cutStr(in, buf, ',', i++); message = appFindMessage(buf); @@ -656,6 +669,19 @@ item->message = message; item->pressed = btnReleased; + if (item->type == itRPotmeter) { + mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] start: %d,%d / stop: %d,%d\n", x0, y0, x1, y1); + + item->zeropoint = appRadian(item, x0, y0); + item->arclength = appRadian(item, x1, y1) - item->zeropoint; + + if (item->arclength < 0.0) + item->arclength += 2 * M_PI; + // else check if radians of (x0,y0) and (x1,y1) only differ below threshold + else if (item->arclength < 0.05) + item->arclength = 2 * M_PI; + } + item->Bitmap.Image = NULL; if (strcmp(phfname, "NULL") != 0) { @@ -735,6 +761,29 @@ } /** + * @brief Parse a @a rpotmeter definition. + * + * Syntax: rpotmeter=button,bwidth,bheight,phases,numphases,x0,y0,x1,y1,default,x,y,width,height,message + * + * @param in definition to be analyzed + * + * @return 0 (ok) or 1 (error) + */ +static int item_rpotmeter(char *in) +{ + guiItem *item; + + item = next_item(); + + if (!item) + return 1; + + item->type = itRPotmeter; + + return parse_potmeter(item, in); +} + +/** * @brief Parse a @a potmeter definition. * * Syntax: potmeter=phases,numphases,default,x,y,width,height,message @@ -1075,6 +1124,7 @@ { "menu", item_menu }, { "pimage", item_pimage }, { "potmeter", item_potmeter }, // legacy + { "rpotmeter", item_rpotmeter }, { "section", item_section }, { "selected", item_selected }, { "slabel", item_slabel }, diff -r 2ef6693131f7 -r 84c93a60ead3 gui/ui/main.c --- a/gui/ui/main.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/ui/main.c Sat Apr 12 23:29:29 2014 +0000 @@ -18,6 +18,7 @@ /* main window */ +#include #include #include #include @@ -95,10 +96,13 @@ static int itemtype = 0; int i; guiItem * item = NULL; + static double prev_point; + double point; float value = 0.0f; static int SelectedItem = -1; int currentselected = -1; + static int endstop; for ( i=0;i <= guiApp.IndexOfMainItems;i++ ) if ( ( guiApp.mainItems[i].pressed != btnDisabled )&& @@ -135,6 +139,13 @@ { item->pressed=btnDisabled; } break; }*/ + if ( itemtype == itRPotmeter ) + { + prev_point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint; + if ( prev_point < 0.0 ) prev_point+=2*M_PI; + if ( prev_point <= item->arclength ) endstop=False; + else endstop=STOPPED_AT_0 + STOPPED_AT_100; // block movement + } break; case wsRLMouseButton: boxMoved=False; @@ -154,6 +165,12 @@ case itVPotmeter: value=100.0 - 100.0 * ( Y - item->y ) / item->height; break; + case itRPotmeter: + if ( endstop ) { itemtype=0; return; } + point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint; + if ( point < 0.0 ) point+=2*M_PI; + value=100.0 * point / item->arclength; + break; } uiEvent( item->message,value ); itemtype=0; @@ -170,7 +187,7 @@ if (currentselected != - 1) { item=&guiApp.mainItems[currentselected]; - if ( ( item->type == itHPotmeter )||( item->type == itVPotmeter ) ) + if ( ( item->type == itHPotmeter )||( item->type == itVPotmeter )||( item->type == itRPotmeter ) ) { item->value=constrain(item->value + value); uiEvent( item->message,item->value ); @@ -189,6 +206,45 @@ case itPRMButton: if (guiApp.menuIsPresent) guiApp.menuWindow.MouseHandler( 0,RX,RY,0,0 ); break; + case itRPotmeter: + point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint; + if ( point < 0.0 ) point+=2*M_PI; + if ( item->arclength < 2 * M_PI ) + /* a potmeter with separated 0% and 100% positions */ + { + value=item->value; + if ( point - prev_point > M_PI ) + /* turned beyond the 0% position */ + { + if ( !endstop ) + { + endstop=STOPPED_AT_0; + value=0.0f; + } + } + else if ( prev_point - point > M_PI ) + /* turned back from beyond the 0% position */ + { + if ( endstop == STOPPED_AT_0 ) endstop=False; + } + else if ( prev_point <= item->arclength && point > item->arclength ) + /* turned beyond the 100% position */ + { + if ( !endstop ) + { + endstop=STOPPED_AT_100; + value=100.0f; + } + } + else if ( prev_point > item->arclength && point <= item->arclength ) + /* turned back from beyond the 100% position */ + { + if ( endstop == STOPPED_AT_100 ) endstop=False; + } + } + if ( !endstop ) value=100.0 * point / item->arclength; + prev_point=point; + goto potihandled; case itVPotmeter: value=100.0 - 100.0 * ( Y - item->y ) / item->height; goto potihandled; diff -r 2ef6693131f7 -r 84c93a60ead3 gui/ui/playbar.c --- a/gui/ui/playbar.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/ui/playbar.c Sat Apr 12 23:29:29 2014 +0000 @@ -18,6 +18,7 @@ /* playbar window */ +#include #include #include #include @@ -123,10 +124,13 @@ static int itemtype = 0; int i; guiItem * item = NULL; + static double prev_point; + double point; float value = 0.0f; static int SelectedItem = -1; int currentselected = -1; + static int endstop; for ( i=0;i <= guiApp.IndexOfPlaybarItems;i++ ) if ( ( guiApp.playbarItems[i].pressed != btnDisabled )&& @@ -162,6 +166,12 @@ ( ( item->message == evPauseSwitchToPlay && item->message == evPlaySwitchToPause ) ) ) ) { item->pressed=btnDisabled; } break; + case itRPotmeter: + prev_point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint; + if ( prev_point < 0.0 ) prev_point+=2*M_PI; + if ( prev_point <= item->arclength ) endstop=False; + else endstop=STOPPED_AT_0 + STOPPED_AT_100; // block movement + break; } break; @@ -183,6 +193,12 @@ case itVPotmeter: value=100.0 - 100.0 * ( Y - item->y ) / item->height; break; + case itRPotmeter: + if ( endstop ) { itemtype=0; return; } + point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint; + if ( point < 0.0 ) point+=2*M_PI; + value=100.0 * point / item->arclength; + break; } uiEvent( item->message,value ); @@ -195,7 +211,7 @@ if (currentselected != - 1) { item=&guiApp.playbarItems[currentselected]; - if ( ( item->type == itHPotmeter )||( item->type == itVPotmeter ) ) + if ( ( item->type == itHPotmeter )||( item->type == itVPotmeter )||( item->type == itRPotmeter ) ) { item->value=constrain(item->value + value); uiEvent( item->message,item->value ); @@ -210,6 +226,45 @@ case itPRMButton: if (guiApp.menuIsPresent) guiApp.menuWindow.MouseHandler( 0,RX,RY,0,0 ); break; + case itRPotmeter: + point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint; + if ( point < 0.0 ) point+=2*M_PI; + if ( item->arclength < 2 * M_PI ) + /* a potmeter with separated 0% and 100% positions */ + { + value=item->value; + if ( point - prev_point > M_PI ) + /* turned beyond the 0% position */ + { + if ( !endstop ) + { + endstop=STOPPED_AT_0; + value=0.0f; + } + } + else if ( prev_point - point > M_PI ) + /* turned back from beyond the 0% position */ + { + if ( endstop == STOPPED_AT_0 ) endstop=False; + } + else if ( prev_point <= item->arclength && point > item->arclength ) + /* turned beyond the 100% position */ + { + if ( !endstop ) + { + endstop=STOPPED_AT_100; + value=100.0f; + } + } + else if ( prev_point > item->arclength && point <= item->arclength ) + /* turned back from beyond the 100% position */ + { + if ( endstop == STOPPED_AT_100 ) endstop=False; + } + } + if ( !endstop ) value=100.0 * point / item->arclength; + prev_point=point; + goto potihandled; case itVPotmeter: value=100.0 - 100.0 * ( Y - item->y ) / item->height; goto potihandled; diff -r 2ef6693131f7 -r 84c93a60ead3 gui/ui/render.c --- a/gui/ui/render.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/ui/render.c Sat Apr 12 23:29:29 2014 +0000 @@ -21,6 +21,7 @@ * @brief GUI rendering */ +#include #include #include #include @@ -35,6 +36,7 @@ #include "access_mpcontext.h" #include "help_mp.h" #include "libavutil/avstring.h" +#include "libavutil/common.h" #include "osdep/timer.h" #include "stream/stream.h" @@ -479,6 +481,29 @@ PutImage(item->x, item->y + (item->height - item->pbheight) * (1.0 - item->value / 100.0), db, dw, &item->Mask, 3, index, True); break; + case itRPotmeter: + + PutImage(item->x, item->y, db, dw, &item->Bitmap, item->numphases, (item->numphases - 1) * item->value / 100.0, True); + + if (item->Mask.Image) { + double radius, radian; + int y; + + // keep the button inside the potmeter outline + radius = (FFMIN(item->width, item->height) - FFMAX(item->pbwidth, item->pbheight)) / 2.0; + + radian = item->value / 100.0 * item->arclength + item->zeropoint; + + // coordinates plus a correction for a non-square item + // (remember: both axes are mirrored, we have a clockwise radian) + x = radius * (1 + cos(radian)) + FFMAX(0, (item->width - item->height) / 2.0) + 0.5; + y = radius * (1 + sin(radian)) + FFMAX(0, (item->height - item->width) / 2.0) + 0.5; + + PutImage(item->x + x, item->y + y, db, dw, &item->Mask, 3, index, True); + } + + break; + case itSLabel: if (item->width == -1) diff -r 2ef6693131f7 -r 84c93a60ead3 gui/ui/ui.h --- a/gui/ui/ui.h Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/ui/ui.h Sat Apr 12 23:29:29 2014 +0000 @@ -19,6 +19,14 @@ #ifndef MPLAYER_GUI_UI_H #define MPLAYER_GUI_UI_H +/// End stops of a rotary potentiometer (::itRPotmeter) +enum +{ + NOT_STOPPED, + STOPPED_AT_0, + STOPPED_AT_100 +}; + extern unsigned char * menuDrawBuffer; extern int mainVisible; diff -r 2ef6693131f7 -r 84c93a60ead3 gui/win32/gui.c --- a/gui/win32/gui.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/win32/gui.c Sat Apr 12 23:29:29 2014 +0000 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,19 @@ return "?"; } +double appRadian (widget *item, int x, int y) +{ + double tx, ty; + + // transform the center to (0,0) + tx = x - item->wwidth / 2.0; + ty = y - item->wheight / 2.0; + + // the y-axis is upside down and must be mirrored + // the x-axis is being mirrored for a clockwise radian + return (tx == 0.0 && ty == 0.0 ? 0.0 : atan2(-ty, -tx) + M_PI); +} + static void console_toggle(gui_t *gui) { if (console_state) @@ -304,10 +318,10 @@ if(!hwnd) return; - /* load all hpotmeters vpotmeters pimages */ + /* load all hpotmeters vpotmeters rpotmeters pimages */ for(i=0; iskin->widgetcount; i++) { - if(gui->skin->widgets[i]->type == tyHpotmeter || gui->skin->widgets[i]->type == tyVpotmeter || gui->skin->widgets[i]->type == tyPimage) + if(gui->skin->widgets[i]->type == tyHpotmeter || gui->skin->widgets[i]->type == tyVpotmeter || gui->skin->widgets[i]->type == tyRpotmeter || gui->skin->widgets[i]->type == tyPimage) { if(gui->skin->widgets[i]->msg == evSetVolume) gui->skin->widgets[i]->value = guiInfo.Volume; @@ -695,6 +709,8 @@ /* Window Proc for the gui Window */ static LRESULT CALLBACK EventProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + static double prev_point; + static int endstop; gui_t *gui = (gui_t *) GetWindowLongPtr(hWnd, GWLP_USERDATA); /* Avoid processing when then window doesn't match gui mainwindow */ @@ -833,6 +849,14 @@ renderwidget(gui->skin, get_drawground(hWnd), gui->activewidget, 0); RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); handlemsg(hWnd, gui->activewidget->msg); + + if(gui->activewidget->type == tyRpotmeter) + { + prev_point = appRadian(gui->activewidget, gui->mousewx, gui->mousewy) - gui->activewidget->zeropoint; + if(prev_point < 0.0) prev_point += 2 * M_PI; + if(prev_point <= gui->activewidget->arclength) endstop=FALSE; + else endstop=STOPPED_AT_0 + STOPPED_AT_100; // block movement + } } break; } @@ -942,8 +966,49 @@ item->y = GET_Y_LPARAM(lParam) - gui->mousewy; item->value = 100.0 - 100.0 * (item->y - item->wy) / (item->wheight - item->height); } + if(item->type == tyRpotmeter) + { + double point; - if((item->type == tyHpotmeter) || (item->type == tyVpotmeter)) + point = appRadian(item, GET_X_LPARAM(lParam) - gui->activewidget->x, GET_Y_LPARAM(lParam) - gui->activewidget->y) - item->zeropoint; + if(point < 0.0) point += 2 * M_PI; + if(item->arclength < 2 * M_PI) + /* a potmeter with separated 0% and 100% positions */ + { + if(point - prev_point > M_PI) + /* turned beyond the 0% position */ + { + if(!endstop) + { + endstop = STOPPED_AT_0; + item->value = 0.0f; + } + } + else if(prev_point - point > M_PI) + /* turned back from beyond the 0% position */ + { + if(endstop == STOPPED_AT_0) endstop = FALSE; + } + else if(prev_point <= item->arclength && point > item->arclength) + /* turned beyond the 100% position */ + { + if (!endstop) + { + endstop = STOPPED_AT_100; + item->value = 100.0f; + } + } + else if(prev_point > item->arclength && point <= item->arclength) + /* turned back from beyond the 100% position */ + { + if(endstop == STOPPED_AT_100) endstop = FALSE; + } + } + if(!endstop) item->value = 100.0 * point / item->arclength; + prev_point = point; + } + + if((item->type == tyHpotmeter) || (item->type == tyVpotmeter) || (item->type == tyRpotmeter)) { /* Bound checks */ if(item->value > 100.0f) diff -r 2ef6693131f7 -r 84c93a60ead3 gui/win32/gui.h --- a/gui/win32/gui.h Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/win32/gui.h Sat Apr 12 23:29:29 2014 +0000 @@ -107,6 +107,7 @@ int parse_filename(char *file, play_tree_t *playtree, m_config_t *mconfig, int clear); void capitalize(char *fname); LPSTR acp(LPCSTR utf8); +double appRadian(widget *item, int x, int y); void renderinfobox(skin_t *skin, window_priv_t *priv); void renderwidget(skin_t *skin, image *dest, widget *item, int state); diff -r 2ef6693131f7 -r 84c93a60ead3 gui/win32/skinload.c --- a/gui/win32/skinload.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/win32/skinload.c Sat Apr 12 23:29:29 2014 +0000 @@ -25,12 +25,15 @@ #include #include #include +#include #include #include "mp_msg.h" #include "help_mp.h" #include "cpudetect.h" #include "libswscale/swscale.h" +#include "libavutil/attributes.h" +#include "libavutil/common.h" #include "libavutil/imgutils.h" #include "gui.h" #include "gui/util/mem.h" @@ -364,12 +367,13 @@ (mywidget->bitmap[0]) ? mywidget->bitmap[0]->name : NULL, mywidget->x, mywidget->y, mywidget->width, mywidget->height, mywidget->msg); } - else if(!strncmp(desc, "hpotmeter", 9) || !strncmp(desc, "vpotmeter", 9) || /* legacy */ !strncmp(desc, "potmeter", 8)) + else if(!strncmp(desc, "hpotmeter", 9) || !strncmp(desc, "vpotmeter", 9) || !strncmp(desc, "rpotmeter", 9) || /* legacy */ !strncmp(desc, "potmeter", 8)) { int base = counttonextchar(desc, '=') + 1; - int i; + int i, av_uninit(x0), av_uninit(y0), av_uninit(x1), av_uninit(y1); /* hpotmeter = button, bwidth, bheight, phases, numphases, default, X, Y, width, height, message */ if(!strncmp(desc, "vpotmeter", 9)) mywidget->type = tyVpotmeter; + else if(!strncmp(desc, "rpotmeter", 9)) mywidget->type = tyRpotmeter; else mywidget->type = tyHpotmeter; if (*desc == 'p') { @@ -388,6 +392,15 @@ } mywidget->bitmap[1] = pngRead(skin, findnextstring(temp, desc, &base)); mywidget->phases = atoi(findnextstring(temp, desc, &base)); + + if (*desc == 'r') + { + x0 = atoi(findnextstring(temp, desc, &base)); + y0 = atoi(findnextstring(temp, desc, &base)); + x1 = atoi(findnextstring(temp, desc, &base)); + y1 = atoi(findnextstring(temp, desc, &base)); + } + mywidget->value = atof(findnextstring(temp, desc, &base)); mywidget->x = mywidget->wx = atoi(findnextstring(temp, desc, &base)); mywidget->y = mywidget->wy = atoi(findnextstring(temp, desc, &base)); @@ -406,12 +419,24 @@ break; } } - mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[SKIN] [ITEM] %s %s %i %i %s %i %f %i %i %i %i msg %i\n", - (mywidget->type == tyHpotmeter) ? "[HPOTMETER]" : "[VPOTMETER]", + if (*desc == 'r') + { + mywidget->zeropoint = appRadian(mywidget, x0, y0); + mywidget->arclength = appRadian(mywidget, x1, y1) - mywidget->zeropoint; + + if (mywidget->arclength < 0.0) mywidget->arclength += 2 * M_PI; + // else check if radians of (x0,y0) and (x1,y1) only differ below threshold + else if (mywidget->arclength < 0.05) mywidget->arclength = 2 * M_PI; + } + mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[SKIN] [ITEM] %s %s %i %i %s %i ", + (mywidget->type == tyHpotmeter) ? "[HPOTMETER]" : (mywidget->type == tyVpotmeter) ? "[VPOTMETER]" : "[RPOTMETER]", (mywidget->bitmap[0]) ? mywidget->bitmap[0]->name : NULL, mywidget->width, mywidget->height, (mywidget->bitmap[1]) ? mywidget->bitmap[1]->name : NULL, - mywidget->phases, mywidget->value, + mywidget->phases); + if (*desc == 'r') + mp_msg(MSGT_GPLAYER, MSGL_DBG2, "%i,%i %i,%i ", x0, y0, x1, y1); + mp_msg(MSGT_GPLAYER, MSGL_DBG2, "%f %i %i %i %i msg %i\n", mywidget->value, mywidget->wx, mywidget->wy, mywidget->wwidth, mywidget->wwidth, mywidget->msg); if (mywidget->bitmap[0] == NULL || mywidget->width == 0 || mywidget->height == 0) @@ -420,6 +445,14 @@ mywidget->width = mywidget->wwidth; mywidget->height = mywidget->wheight; } + if (*desc == 'r') + { + mywidget->maxwh = FFMAX(mywidget->width, mywidget->height); + + // clickedinsidewidget() checks with width/height, so set it + mywidget->width = mywidget->wwidth; + mywidget->height = mywidget->wheight; + } } else if(!strncmp(desc, "pimage", 6)) { diff -r 2ef6693131f7 -r 84c93a60ead3 gui/win32/skinload.h --- a/gui/win32/skinload.h Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/win32/skinload.h Sat Apr 12 23:29:29 2014 +0000 @@ -61,12 +61,15 @@ int width, height; /* width and height of the button */ int wwidth, wheight; /* width and height of the widget */ // --- + int maxwh; // --- int msg, msg2; int pressed, tmp; int key, key2; int phases; float value; + double zeropoint; + double arclength; image *bitmap[2]; /* Associated image(s) in imagepool */ // --- font_t *font; @@ -121,6 +124,7 @@ #define tyMenu 6 #define tySlabel 7 #define tyDlabel 8 +#define tyRpotmeter 9 /* --- Window types --- */ diff -r 2ef6693131f7 -r 84c93a60ead3 gui/win32/widgetrender.c --- a/gui/win32/widgetrender.c Fri Apr 11 09:34:31 2014 +0000 +++ b/gui/win32/widgetrender.c Sat Apr 12 23:29:29 2014 +0000 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "gui/util/bitmap.h" @@ -34,6 +35,7 @@ #include "access_mpcontext.h" #include "help_mp.h" #include "libavutil/avstring.h" +#include "libavutil/common.h" #include "stream/stream.h" #define MAX_LABELSIZE 250 @@ -409,11 +411,13 @@ if(!dest) return; if((item->type == tyButton) || (item->type == tyHpotmeter) || (item->type == tyVpotmeter) || (item->type == tyPimage)) img = item->bitmap[0]; + if(item->type == tyRpotmeter) + img = item->bitmap[1]; if(!img) return; y = item->y; - if(item->type == tyPimage || /* legacy (potmeter) */ (item->type == tyHpotmeter && item->width == item->wwidth)) + if(item->type == tyPimage || /* legacy (potmeter) */ (item->type == tyHpotmeter && item->width == item->wwidth) || item->type == tyRpotmeter) { height = img->height / item->phases; y = height * (int)(item->value * item->phases / 100); @@ -430,7 +434,7 @@ if(item->type == tyButton) render(skin->desktopbpp, dest, find_background(skin,item), item->x, item->y, item->x, item->y, img->width, height, 1); - if((item->type == tyHpotmeter) || (item->type == tyVpotmeter) || (item->type == tyPimage)) + if((item->type == tyHpotmeter) || (item->type == tyVpotmeter) || (item->type == tyRpotmeter) || (item->type == tyPimage)) { if(item->type == tyVpotmeter) { @@ -438,6 +442,13 @@ render(skin->desktopbpp, dest, find_background(skin, item), item->wx, item->wy, item->wx, item->wy, item->width, item->wheight, 1); item->y = (100 - item->value) * (item->wheight-item->height) / 100 + item->wy; } + else if(item->type == tyRpotmeter) + { + /* repaint the area behind the rpotmeter */ + render(skin->desktopbpp, dest, find_background(skin, item), item->wx, item->wy, item->wx, item->wy, item->wwidth, item->wheight, 1); + item->x = item->wx; + item->y = item->wy; + } else { /* repaint the area behind the slider */ @@ -446,4 +457,31 @@ } } render(skin->desktopbpp, dest, img, item->x, item->y, 0, y, img->width, height, 1); + + /* rpotmeter button */ + if(item->type == tyRpotmeter && item->bitmap[0] != item->bitmap[1]) + { + img = item->bitmap[0]; + + if(img) + { + double radius, radian; + int ix, iy; + + // keep the button inside the potmeter outline + radius = (FFMIN(item->wwidth, item->wheight) - item->maxwh) / 2.0; + + radian = item->value / 100.0 * item->arclength + item->zeropoint; + + // coordinates plus a correction for a non-square item + // (remember: both axes are mirrored, we have a clockwise radian) + ix = item->wx + radius * (1 + cos(radian)) + FFMAX(0, (item->wwidth - item->wheight) / 2.0) + 0.5; + iy = item->wy + radius * (1 + sin(radian)) + FFMAX(0, (item->wheight - item->wwidth) / 2.0) + 0.5; + + height = img->height / 3; + y = state * height; + + render(skin->desktopbpp, dest, img, ix, iy, 0, y, img->width, height, 1); + } + } }