ui/motif/text.c

changeset 925
df27741d02b5
parent 907
d54a72c94320
child 927
b8c0f718b141
equal deleted inserted replaced
924:6c6e97e06009 925:df27741d02b5
30 #include <stdlib.h> 30 #include <stdlib.h>
31 #include <unistd.h> 31 #include <unistd.h>
32 32
33 #include "text.h" 33 #include "text.h"
34 #include "container.h" 34 #include "container.h"
35 #include "pathbar.h"
35 36
36 #include <cx/string.h> 37 #include <cx/string.h>
37 38
38 39
39 /* ------------------------------ Text Area ------------------------------ */ 40 /* ------------------------------ Text Area ------------------------------ */
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

mercurial