| 506 } |
507 } |
| 507 str->value.ptr = NULL; |
508 str->value.ptr = NULL; |
| 508 } |
509 } |
| 509 |
510 |
| 510 |
511 |
| 511 |
|
| 512 |
|
| 513 |
|
| 514 /* -------------------- path bar -------------------- */ |
|
| 515 |
|
| 516 #define XNECreateText(parent,name,args,count) XmCreateTextField(parent,name,args,count) |
|
| 517 #define XNETextSetString(widget,value) XmTextFieldSetString(widget,value) |
|
| 518 #define XNETextGetString(widget) XmTextFieldGetString(widget) |
|
| 519 #define XNETextGetLastPosition(widget) XmTextFieldGetLastPosition(widget) |
|
| 520 #define XNETextSetInsertionPosition(widget, i) XmTextFieldSetInsertionPosition(widget, i) |
|
| 521 #define XNETextSetSelection(w, f, l, t) XmTextFieldSetSelection(w, f, l, t) |
|
| 522 |
|
| 523 typedef void(*updatedir_callback)(void*,char*,int); |
|
| 524 |
|
| 525 typedef struct PathBar { |
|
| 526 Widget widget; |
|
| 527 Widget textfield; |
|
| 528 |
|
| 529 Widget focus_widget; |
|
| 530 |
|
| 531 Widget left; |
|
| 532 Widget right; |
|
| 533 Dimension lw; |
|
| 534 Dimension rw; |
|
| 535 |
|
| 536 int shift; |
|
| 537 |
|
| 538 UiPathElm *current_pathelms; |
|
| 539 Widget *pathSegments; |
|
| 540 size_t numSegments; |
|
| 541 size_t segmentAlloc; |
|
| 542 |
|
| 543 char *path; |
|
| 544 int selection; |
|
| 545 Boolean input; |
|
| 546 |
|
| 547 int focus; |
|
| 548 |
|
| 549 updatedir_callback updateDir; |
|
| 550 void *updateDirData; |
|
| 551 |
|
| 552 ui_pathelm_func getpathelm; |
|
| 553 void *getpathelmdata; |
|
| 554 } PathBar; |
|
| 555 |
|
| 556 void PathBarSetPath(PathBar *bar, const char *path); |
|
| 557 |
|
| 558 void pathbar_resize(Widget w, PathBar *p, XtPointer d) |
|
| 559 { |
|
| 560 Dimension width, height; |
|
| 561 XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); |
|
| 562 |
|
| 563 Dimension *segW = (void*)XtCalloc(p->numSegments, sizeof(Dimension)); |
|
| 564 |
|
| 565 Dimension maxHeight = 0; |
|
| 566 |
|
| 567 /* get width/height from all widgets */ |
|
| 568 Dimension pathWidth = 0; |
|
| 569 for(int i=0;i<p->numSegments;i++) { |
|
| 570 Dimension segWidth; |
|
| 571 Dimension segHeight; |
|
| 572 XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); |
|
| 573 segW[i] = segWidth; |
|
| 574 pathWidth += segWidth; |
|
| 575 if(segHeight > maxHeight) { |
|
| 576 maxHeight = segHeight; |
|
| 577 } |
|
| 578 } |
|
| 579 Dimension tfHeight; |
|
| 580 XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); |
|
| 581 if(tfHeight > maxHeight) { |
|
| 582 maxHeight = tfHeight; |
|
| 583 } |
|
| 584 |
|
| 585 Boolean arrows = False; |
|
| 586 if(pathWidth + 10 > width) { |
|
| 587 arrows = True; |
|
| 588 pathWidth += p->lw + p->rw; |
|
| 589 } |
|
| 590 |
|
| 591 /* calc max visible widgets */ |
|
| 592 int start = 0; |
|
| 593 if(arrows) { |
|
| 594 Dimension vis = p->lw+p->rw; |
|
| 595 for(int i=p->numSegments;i>0;i--) { |
|
| 596 Dimension segWidth = segW[i-1]; |
|
| 597 if(vis + segWidth + 10 > width) { |
|
| 598 start = i; |
|
| 599 arrows = True; |
|
| 600 break; |
|
| 601 } |
|
| 602 vis += segWidth; |
|
| 603 } |
|
| 604 } else { |
|
| 605 p->shift = 0; |
|
| 606 } |
|
| 607 |
|
| 608 int leftShift = 0; |
|
| 609 if(p->shift < 0) { |
|
| 610 if(start + p->shift < 0) { |
|
| 611 leftShift = start; |
|
| 612 start = 0; |
|
| 613 p->shift = -leftShift; |
|
| 614 } else { |
|
| 615 leftShift = -p->shift; /* negative shift */ |
|
| 616 start += p->shift; |
|
| 617 } |
|
| 618 } |
|
| 619 |
|
| 620 int x = 0; |
|
| 621 if(arrows) { |
|
| 622 XtManageChild(p->left); |
|
| 623 XtManageChild(p->right); |
|
| 624 x = p->lw; |
|
| 625 } else { |
|
| 626 XtUnmanageChild(p->left); |
|
| 627 XtUnmanageChild(p->right); |
|
| 628 } |
|
| 629 |
|
| 630 for(int i=0;i<p->numSegments;i++) { |
|
| 631 if(i >= start && i < p->numSegments - leftShift && !p->input) { |
|
| 632 XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); |
|
| 633 x += segW[i]; |
|
| 634 XtManageChild(p->pathSegments[i]); |
|
| 635 } else { |
|
| 636 XtUnmanageChild(p->pathSegments[i]); |
|
| 637 } |
|
| 638 } |
|
| 639 |
|
| 640 if(arrows) { |
|
| 641 XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); |
|
| 642 XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); |
|
| 643 } |
|
| 644 |
|
| 645 free(segW); |
|
| 646 |
|
| 647 Dimension rw, rh; |
|
| 648 XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); |
|
| 649 |
|
| 650 XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL); |
|
| 651 } |
|
| 652 |
|
| 653 static void pathbarActivateTF(PathBar *p) |
|
| 654 { |
|
| 655 XtUnmanageChild(p->left); |
|
| 656 XtUnmanageChild(p->right); |
|
| 657 XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0); |
|
| 658 XtManageChild(p->textfield); |
|
| 659 p->input = 1; |
|
| 660 |
|
| 661 XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); |
|
| 662 |
|
| 663 pathbar_resize(p->widget, p, NULL); |
|
| 664 } |
|
| 665 |
|
| 666 void PathBarActivateTextfield(PathBar *p) |
|
| 667 { |
|
| 668 p->focus = 1; |
|
| 669 pathbarActivateTF(p); |
|
| 670 } |
|
| 671 |
|
| 672 void pathbar_input(Widget w, PathBar *p, XtPointer c) |
|
| 673 { |
|
| 674 XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; |
|
| 675 XEvent *xevent = cbs->event; |
|
| 676 |
|
| 677 if (cbs->reason == XmCR_INPUT) { |
|
| 678 if (xevent->xany.type == ButtonPress) { |
|
| 679 p->focus = 0; |
|
| 680 pathbarActivateTF(p); |
|
| 681 } |
|
| 682 } |
|
| 683 } |
|
| 684 |
|
| 685 void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) |
|
| 686 { |
|
| 687 if(--p->focus < 0) { |
|
| 688 p->input = False; |
|
| 689 XtUnmanageChild(p->textfield); |
|
| 690 } |
|
| 691 } |
|
| 692 |
|
| 693 static cxmutstr concat_path_s(cxstring base, cxstring path) { |
|
| 694 if(!path.ptr) { |
|
| 695 path = CX_STR(""); |
|
| 696 } |
|
| 697 |
|
| 698 int add_separator = 0; |
|
| 699 if(base.length != 0 && base.ptr[base.length-1] == '/') { |
|
| 700 if(path.ptr[0] == '/') { |
|
| 701 base.length--; |
|
| 702 } |
|
| 703 } else { |
|
| 704 if(path.length == 0 || path.ptr[0] != '/') { |
|
| 705 add_separator = 1; |
|
| 706 } |
|
| 707 } |
|
| 708 |
|
| 709 cxmutstr url; |
|
| 710 if(add_separator) { |
|
| 711 url = cx_strcat(3, base, CX_STR("/"), path); |
|
| 712 } else { |
|
| 713 url = cx_strcat(2, base, path); |
|
| 714 } |
|
| 715 |
|
| 716 return url; |
|
| 717 } |
|
| 718 |
|
| 719 static char* ConcatPath(const char *path1, const char *path2) { |
|
| 720 return concat_path_s(cx_str(path1), cx_str(path2)).ptr; |
|
| 721 } |
|
| 722 |
|
| 723 void pathbar_pathinput(Widget w, PathBar *p, XtPointer d) |
|
| 724 { |
|
| 725 char *newpath = XNETextGetString(p->textfield); |
|
| 726 if(newpath) { |
|
| 727 if(newpath[0] == '~') { |
|
| 728 char *p = newpath+1; |
|
| 729 char *home = getenv("HOME"); |
|
| 730 char *cp = ConcatPath(home, p); |
|
| 731 XtFree(newpath); |
|
| 732 newpath = cp; |
|
| 733 } else if(newpath[0] != '/') { |
|
| 734 char curdir[2048]; |
|
| 735 curdir[0] = 0; |
|
| 736 getcwd(curdir, 2048); |
|
| 737 char *cp = ConcatPath(curdir, newpath); |
|
| 738 XtFree(newpath); |
|
| 739 newpath = cp; |
|
| 740 } |
|
| 741 |
|
| 742 /* update path */ |
|
| 743 PathBarSetPath(p, newpath); |
|
| 744 if(p->updateDir) { |
|
| 745 p->updateDir(p->updateDirData, newpath, -1); |
|
| 746 } |
|
| 747 XtFree(newpath); |
|
| 748 |
|
| 749 /* hide textfield and show path as buttons */ |
|
| 750 XtUnmanageChild(p->textfield); |
|
| 751 pathbar_resize(p->widget, p, NULL); |
|
| 752 |
|
| 753 if(p->focus_widget) { |
|
| 754 XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT); |
|
| 755 } |
|
| 756 } |
|
| 757 } |
|
| 758 |
|
| 759 void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) |
|
| 760 { |
|
| 761 p->shift--; |
|
| 762 pathbar_resize(p->widget, p, NULL); |
|
| 763 } |
|
| 764 |
|
| 765 void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) |
|
| 766 { |
|
| 767 if(p->shift < 0) { |
|
| 768 p->shift++; |
|
| 769 } |
|
| 770 pathbar_resize(p->widget, p, NULL); |
|
| 771 } |
|
| 772 |
|
| 773 static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { |
|
| 774 PathBar *pb = data; |
|
| 775 if(event->type == KeyReleaseMask) { |
|
| 776 if(event->xkey.keycode == 9) { |
|
| 777 XtUnmanageChild(pb->textfield); |
|
| 778 pathbar_resize(pb->widget, pb, NULL); |
|
| 779 *dispatch = False; |
|
| 780 } else if(event->xkey.keycode == 36) { |
|
| 781 pathbar_pathinput(pb->textfield, pb, NULL); |
|
| 782 *dispatch = False; |
|
| 783 } |
|
| 784 } |
|
| 785 } |
|
| 786 |
|
| 787 PathBar* CreatePathBar(Widget parent, ArgList args, int n) |
|
| 788 { |
|
| 789 PathBar *bar = (PathBar*)XtMalloc(sizeof(PathBar)); |
|
| 790 bar->path = NULL; |
|
| 791 bar->updateDir = NULL; |
|
| 792 bar->updateDirData = NULL; |
|
| 793 |
|
| 794 bar->focus_widget = NULL; |
|
| 795 |
|
| 796 bar->getpathelm = NULL; |
|
| 797 bar->getpathelmdata = NULL; |
|
| 798 bar->current_pathelms = NULL; |
|
| 799 |
|
| 800 bar->shift = 0; |
|
| 801 |
|
| 802 XtSetArg(args[n], XmNmarginWidth, 0); n++; |
|
| 803 XtSetArg(args[n], XmNmarginHeight, 0); n++; |
|
| 804 bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); |
|
| 805 XtAddCallback( |
|
| 806 bar->widget, |
|
| 807 XmNresizeCallback, |
|
| 808 (XtCallbackProc)pathbar_resize, |
|
| 809 bar); |
|
| 810 XtAddCallback( |
|
| 811 bar->widget, |
|
| 812 XmNinputCallback, |
|
| 813 (XtCallbackProc)pathbar_input, |
|
| 814 bar); |
|
| 815 |
|
| 816 Arg a[4]; |
|
| 817 XtSetArg(a[0], XmNshadowThickness, 0); |
|
| 818 XtSetArg(a[1], XmNx, 0); |
|
| 819 XtSetArg(a[2], XmNy, 0); |
|
| 820 bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3); |
|
| 821 bar->input = 0; |
|
| 822 XtAddCallback( |
|
| 823 bar->textfield, |
|
| 824 XmNlosingFocusCallback, |
|
| 825 (XtCallbackProc)pathbar_losingfocus, |
|
| 826 bar); |
|
| 827 XtAddCallback(bar->textfield, XmNactivateCallback, |
|
| 828 (XtCallbackProc)pathbar_pathinput, bar); |
|
| 829 XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); |
|
| 830 |
|
| 831 XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); |
|
| 832 bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); |
|
| 833 XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); |
|
| 834 bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); |
|
| 835 XtAddCallback( |
|
| 836 bar->left, |
|
| 837 XmNactivateCallback, |
|
| 838 (XtCallbackProc)pathbar_shift_left, |
|
| 839 bar); |
|
| 840 XtAddCallback( |
|
| 841 bar->right, |
|
| 842 XmNactivateCallback, |
|
| 843 (XtCallbackProc)pathbar_shift_right, |
|
| 844 bar); |
|
| 845 |
|
| 846 Pixel bg; |
|
| 847 XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); |
|
| 848 XtVaSetValues(bar->widget, XmNbackground, bg, NULL); |
|
| 849 |
|
| 850 XtManageChild(bar->left); |
|
| 851 XtManageChild(bar->right); |
|
| 852 |
|
| 853 XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); |
|
| 854 XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); |
|
| 855 |
|
| 856 bar->segmentAlloc = 16; |
|
| 857 bar->numSegments = 0; |
|
| 858 bar->pathSegments = (Widget*)XtCalloc(16, sizeof(Widget)); |
|
| 859 |
|
| 860 bar->selection = 0; |
|
| 861 |
|
| 862 return bar; |
|
| 863 } |
|
| 864 |
|
| 865 void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c) |
|
| 866 { |
|
| 867 XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); |
|
| 868 |
|
| 869 int i; |
|
| 870 for(i=0;i<bar->numSegments;i++) { |
|
| 871 if(bar->pathSegments[i] == w) { |
|
| 872 bar->selection = i; |
|
| 873 XmToggleButtonSetState(w, True, False); |
|
| 874 break; |
|
| 875 } |
|
| 876 } |
|
| 877 |
|
| 878 UiPathElm elm = bar->current_pathelms[i]; |
|
| 879 cxmutstr path = cx_strdup(cx_strn(elm.path, elm.path_len)); |
|
| 880 if(bar->updateDir) { |
|
| 881 XNETextSetString(bar->textfield, path.ptr); |
|
| 882 bar->updateDir(bar->updateDirData, path.ptr, i); |
|
| 883 } |
|
| 884 free(path.ptr); |
|
| 885 } |
|
| 886 |
|
| 887 static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { |
|
| 888 for(int i=0;i<nelm;i++) { |
|
| 889 free(elms[i].name); |
|
| 890 free(elms[i].path); |
|
| 891 } |
|
| 892 free(elms); |
|
| 893 } |
|
| 894 |
|
| 895 void PathBarSetPath(PathBar *bar, const char *path) |
|
| 896 { |
|
| 897 if(bar->path) { |
|
| 898 free(bar->path); |
|
| 899 } |
|
| 900 bar->path = strdup(path); |
|
| 901 |
|
| 902 for(int i=0;i<bar->numSegments;i++) { |
|
| 903 XtDestroyWidget(bar->pathSegments[i]); |
|
| 904 } |
|
| 905 XtUnmanageChild(bar->textfield); |
|
| 906 XtManageChild(bar->left); |
|
| 907 XtManageChild(bar->right); |
|
| 908 bar->input = False; |
|
| 909 |
|
| 910 Arg args[4]; |
|
| 911 XmString str; |
|
| 912 |
|
| 913 bar->numSegments = 0; |
|
| 914 |
|
| 915 ui_pathelm_destroy(bar->current_pathelms, bar->numSegments); |
|
| 916 size_t nelm = 0; |
|
| 917 UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata); |
|
| 918 if (!path_elm) { |
|
| 919 return; |
|
| 920 } |
|
| 921 bar->current_pathelms = path_elm; |
|
| 922 bar->numSegments = nelm; |
|
| 923 bar->pathSegments = realloc(bar->pathSegments, nelm * sizeof(Widget*)); |
|
| 924 |
|
| 925 for(int i=0;i<nelm;i++) { |
|
| 926 UiPathElm elm = path_elm[i]; |
|
| 927 |
|
| 928 cxmutstr name = cx_strdup(cx_strn(elm.name, elm.name_len)); |
|
| 929 str = XmStringCreateLocalized(elm.name); |
|
| 930 free(name.ptr); |
|
| 931 |
|
| 932 XtSetArg(args[0], XmNlabelString, str); |
|
| 933 XtSetArg(args[1], XmNfillOnSelect, True); |
|
| 934 XtSetArg(args[2], XmNindicatorOn, False); |
|
| 935 Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3); |
|
| 936 XtAddCallback( |
|
| 937 button, |
|
| 938 XmNvalueChangedCallback, |
|
| 939 (XtCallbackProc)PathBarChangeDir, |
|
| 940 bar); |
|
| 941 XmStringFree(str); |
|
| 942 |
|
| 943 bar->pathSegments[i] = button; |
|
| 944 } |
|
| 945 |
|
| 946 bar->selection = bar->numSegments-1; |
|
| 947 XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); |
|
| 948 |
|
| 949 XNETextSetString(bar->textfield, (char*)path); |
|
| 950 XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield)); |
|
| 951 |
|
| 952 pathbar_resize(bar->widget, bar, NULL); |
|
| 953 } |
|
| 954 |
|
| 955 void PathBarDestroy(PathBar *pathbar) { |
|
| 956 if(pathbar->path) { |
|
| 957 XtFree(pathbar->path); |
|
| 958 } |
|
| 959 XtFree((void*)pathbar->pathSegments); |
|
| 960 XtFree((void*)pathbar); |
|
| 961 } |
|
| 962 |
|
| 963 |
|
| 964 /* ---------------------------- Path Text Field ---------------------------- */ |
512 /* ---------------------------- Path Text Field ---------------------------- */ |
| 965 |
513 |
| 966 static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { |
514 static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { |
| 967 PathBar *pathbar = (PathBar*)data; |
515 PathBar *pathbar = (PathBar*)data; |
| 968 // TODO: check if there is somonething missing |
516 // TODO: check if there is somonething missing |