/* * Copyright 2022 Olaf Wintermann * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "Sidebar.h" #include "SidebarP.h" #include #include #include #include #include "xdnd.h" #include "utils.h" #include "playlist.h" #include static void sidebar_class_init(void); static void sidebar_class_part_init (WidgetClass wc); static void sidebar_init(Widget request, Widget neww, ArgList args, Cardinal *num_args); static void sidebar_destroy(Widget widget); static void sidebar_resize(Widget widget); static void sidebar_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes); static void sidebar_expose(Widget widget, XEvent *event, Region region); static Boolean sidebar_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args); static void sidebar_insert_child(Widget child); Boolean sidebar_acceptfocus(Widget widget, Time *time); static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); static void SelectElmAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); static void PopupAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); static void sidebar_xdnd_callback(Widget w, XtPointer udata, XtPointer cdata); static XtResource resources[] = { }; static XtActionsRec actionslist[] = { {"focusIn", FocusInAP}, {"selectElm", SelectElmAP}, {"popup", PopupAP} }; static char defaultTranslations[] = ": focusIn()\n" ": selectElm()\n" ": popup()"; static XtResource constraints[] = {}; SidebarClassRec sidebarWidgetClassRec = { // Core Class { (WidgetClass)&xmManagerClassRec, "XnSidebar", // class_name sizeof(SidebarRec), // widget_size sidebar_class_init, // class_initialize sidebar_class_part_init, // class_part_initialize FALSE, // class_inited sidebar_init, // initialize NULL, // initialize_hook sidebar_realize, // realize actionslist, // actions XtNumber(actionslist), // num_actions resources, // resources XtNumber(resources), // num_resources NULLQUARK, // xrm_class True, // compress_motion True, // compress_exposure True, // compress_enterleave False, // visible_interest sidebar_destroy, // destroy sidebar_resize, // resize sidebar_expose, // expose sidebar_set_values, // set_values NULL, // set_values_hook XtInheritSetValuesAlmost, // set_values_almost NULL, // get_values_hook sidebar_acceptfocus, // accept_focus XtVersion, // version NULL, // callback_offsets defaultTranslations, // tm_table XtInheritQueryGeometry, // query_geometry XtInheritDisplayAccelerator, // display_accelerator NULL, // extension }, // Composite Class { XtInheritGeometryManager, // geometry_manager XtInheritChangeManaged, // change_managed sidebar_insert_child, // insert_child XtInheritDeleteChild, // delete_child NULL, // extension }, // Constraint Class { constraints, // resources XtNumber(constraints), // num_resources sizeof(XmFormConstraintRec), // constraint_size NULL, // initialize NULL, // destroy NULL, // set_value NULL, // extension }, // XmManager Class { XtInheritTranslations, // translations NULL, // syn_resources 0, // num_syn_resources NULL, // syn_constraint_resources 0, // num_syn_constraint_resources XmInheritParentProcess, // parent_process NULL // extension }, // Sidebar Class { 0 } }; WidgetClass xnSidebarWidgetClass = (WidgetClass)&sidebarWidgetClassRec; static void sidebar_class_init(void) { } static void sidebar_class_part_init (WidgetClass wc) { SidebarClassRec *sidebarClass = (SidebarClassRec*)wc; XmManagerClassRec *managerClass = (XmManagerClassRec*)xmManagerWidgetClass; sidebarClass->constraint_class.initialize = managerClass->constraint_class.initialize; sidebarClass->constraint_class.set_values = managerClass->constraint_class.set_values; } static void sidebar_init(Widget request, Widget neww, ArgList args, Cardinal *num_args) { Sidebar sb = (Sidebar)neww; // initialize everything except XftDraw or other stuff that needs a Window XftColor fg, bg, sel2; fg.color.red = 0; fg.color.green = 0; fg.color.blue = 0; fg.color.alpha = 0xFFFF; bg.color.red = 0xFFFF; bg.color.green = 0xFFFF; bg.color.blue = 0xFFFF; bg.color.alpha = 0xFFFF; sel2.color.red = 0xFFFF; sel2.color.green = 0xFFFF; sel2.color.blue = 0; sel2.color.alpha = 0xFFFF; sb->sidebar.fg = fg; sb->sidebar.bg = bg; sb->sidebar.select2_bg = sel2; sb->sidebar.select2 = -1; sb->sidebar.font = FontFromName(XtDisplay(neww), "Sans:size=11"); if(!sb->sidebar.font) { fprintf(stderr, "Cannot open font.\nAbort.\n"); exit(-1); } XftFont *xftFont = sb->sidebar.font->fonts->font; int padding = 2; int fontheight = xftFont->ascent; sb->sidebar.elmHeight = fontheight + 2*padding; } static void sidebar_destroy(Widget widget) { } static int testresize = 1; static void sidebar_resize(Widget widget) { if(testresize) { XtWidgetGeometry geom; geom.request_mode = CWWidth | CWHeight; geom.width = 200; geom.height = 2000; XtWidgetGeometry result; //XtMakeGeometryRequest(widget, &geom, &result); testresize = 0; } printf("resize\n"); } static void remove_cb(Widget item, XtPointer index, XtPointer cd) { Widget parent = XtParent(item); Sidebar sb = NULL; XtVaGetValues(parent, XmNuserData, &sb, NULL); int currentTrack = sb->sidebar.window->playlist.current_track; cxListRemove(sb->sidebar.window->playlist.tracks, sb->sidebar.select2); if(sb->sidebar.select2 == currentTrack) { PlayListPlayNext(sb->sidebar.window, true); } sb->sidebar.select2 = -1; SidebarRepaint((Widget)sb); } static int xdnd_initialized = 0; static void sidebar_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes) { (xmManagerClassRec.core_class.realize)(widget, mask, attributes); if(!xdnd_initialized) { XdndInit(XtDisplay(widget), XtWidgetToApplicationContext(widget), sidebar_xdnd_callback, widget); } XdndEnable(widget); Screen *screen = widget->core.screen; Visual *visual = screen->root_visual; for(int i=0;indepths;i++) { Depth d = screen->depths[i]; if(d.depth == widget->core.depth) { visual = d.visuals; break; } } Sidebar sb = (Sidebar)widget; sb->sidebar.d = XftDrawCreate( XtDisplay(widget), XtWindow(widget), visual, widget->core.colormap); Dimension w, h; Widget parent = XtParent(widget); XtVaSetValues(parent, XmNbackground, WhitePixelOfScreen(XtScreen(parent)), NULL); XtMakeResizeRequest(widget, 200, 100, &w, &h); XmString s1 = XmStringCreateSimple("Remove"); sb->sidebar.popupMenu = XmVaCreateSimplePopupMenu( widget, "popup", remove_cb, XmVaPUSHBUTTON, s1, 'R', NULL, NULL, NULL); XtVaSetValues(sb->sidebar.popupMenu, XmNuserData, sb, NULL); XmStringFree(s1); } static void sidebar_expose(Widget widget, XEvent *event, Region region) { //printf("expose\n"); Dimension w, h; //XtMakeResizeRequest(widget, 200, 2000, &w, &h); w = widget->core.width; h = widget->core.height; Widget parent = XtParent(widget); Sidebar s = (Sidebar)widget; XftFont *xftFont = s->sidebar.font->fonts->font; CxList *tracks = s->sidebar.window->playlist.tracks; size_t numTracks = tracks->size; int fontheight = xftFont->ascent; int height = s->sidebar.elmHeight; int list_height = numTracks * height; if((list_height > s->core.height) || (w < parent->core.width)) { XtMakeResizeRequest(widget, parent->core.width, list_height + 5, &w, &h); } XftDrawRect(s->sidebar.d, &s->sidebar.bg, 0, 0, w, h); //printf("current track: %d\n", s->sidebar.window->playlist.current_track); CxIterator i = cxListIterator(s->sidebar.window->playlist.tracks); cx_foreach(const char *, elm, i) { const char *name = util_resource_name(elm); XftColor *cg = &s->sidebar.fg; if(i.index == s->sidebar.window->playlist.current_track) { XftDrawRect(s->sidebar.d, &s->sidebar.fg, 0, i.index*height, s->core.width, height); cg = &s->sidebar.bg; } else if(i.index == s->sidebar.select2) { XftDrawRect(s->sidebar.d, &s->sidebar.select2_bg, 0, i.index*height, s->core.width, height); } XftDrawString8(s->sidebar.d, cg, s->sidebar.font->fonts->font, 10, i.index*height + xftFont->ascent, (const FcChar8*)name, strlen(name)); } } static Boolean sidebar_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { Boolean r = False; return r; } static void sidebar_insert_child(Widget child) { (xmManagerClassRec.composite_class.insert_child)(child); } Boolean sidebar_acceptfocus(Widget widget, Time *time) { return 0; } static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) { } static void SelectElmAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) { //printf("btn1\n"); XButtonEvent *e = &event->xbutton; Sidebar s = (Sidebar)w; int selected = e->y / s->sidebar.elmHeight; PlayListPlayTrack(s->sidebar.window, selected); s->sidebar.select2 = -1; SidebarRepaint(w); } static void PopupAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) { XButtonEvent *e = &event->xbutton; Sidebar s = (Sidebar)w; int selected = e->y / s->sidebar.elmHeight; s->sidebar.select2 = selected; SidebarRepaint(w); //printf("btn3\n"); //fflush(stdout); XmMenuPosition(s->sidebar.popupMenu, &event->xbutton); XtManageChild(s->sidebar.popupMenu); } static void open_uri(Sidebar s, cxstring uri) { if(uri.length == 0) { return; } const char *urilist = uri.ptr; size_t len = uri.length; size_t start = 0; if(len > 7 && !memcmp(urilist, "file://", 7)) { start = 7; } int err = 0; // urldecode char *path = malloc(len + 1); int k = 0; for(int i=start;isidebar.window, path); free(path); } static void sidebar_xdnd_callback(Widget w, XtPointer udata, XtPointer cdata) { printf("xdnd\n"); fflush(stdout); Sidebar s = (Sidebar)cdata; cxstring urllist = cx_str(udata); CxStrtokCtx tk = cx_strtok(urllist, cx_str("\r\n"), INT_MAX); cxstring uri; while(cx_strtok_next(&tk, &uri)) { open_uri(s, uri); } SidebarRepaint((Widget)s); } /* --------------------------- public --------------------------- */ Widget CreateSidebar( Widget parent, String name, ArgList arglist, Cardinal argcount) { return XtCreateWidget(name, xnSidebarWidgetClass, parent, arglist, argcount); } void SidebarSetWindow(Widget widget, MainWindow *win) { Sidebar sb = (Sidebar)widget; sb->sidebar.window = win; } void SidebarRepaint(Widget widget) { XClearArea(XtDisplay(widget), XtWindow(widget), 0, 0, widget->core.width, widget->core.height, true); }