ui/gtk/text.c

branch
newapi
changeset 282
3a77b9048664
parent 267
79dd183dd4cb
child 289
6048b20bd46f
equal deleted inserted replaced
281:2533cdebf6ef 282:3a77b9048664
31 #include <string.h> 31 #include <string.h>
32 32
33 #include "text.h" 33 #include "text.h"
34 #include "container.h" 34 #include "container.h"
35 35
36 #include <cx/printf.h>
37
38 #include <gdk/gdkkeysyms.h>
39
36 40
37 #include "../common/types.h" 41 #include "../common/types.h"
38 42
39 static void selection_handler( 43 static void selection_handler(
40 GtkTextBuffer *buf, 44 GtkTextBuffer *buf,
638 str->value.free(str->value.ptr); 642 str->value.free(str->value.ptr);
639 str->value.ptr = NULL; 643 str->value.ptr = NULL;
640 str->value.free = NULL; 644 str->value.free = NULL;
641 } 645 }
642 } 646 }
647
648 // ----------------------- path textfield -----------------------
649
650 // TODO: move to common
651 static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
652 cxstring *pathelms;
653 size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms);
654
655 if (nelm == 0) {
656 *ret_nelm = 0;
657 return NULL;
658 }
659
660 UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm));
661 size_t n = nelm;
662 int j = 0;
663 for (int i = 0; i < nelm; i++) {
664 cxstring c = pathelms[i];
665 if (c.length == 0) {
666 if (i == 0) {
667 c.length = 1;
668 }
669 else {
670 n--;
671 continue;
672 }
673 }
674
675 cxmutstr m = cx_strdup(c);
676 elms[j].name = m.ptr;
677 elms[j].name_len = m.length;
678
679 size_t elm_path_len = c.ptr + c.length - full_path;
680 cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len));
681 elms[j].path = elm_path.ptr;
682 elms[j].path_len = elm_path.length;
683
684 j++;
685 }
686 *ret_nelm = n;
687
688 return elms;
689 }
690
691 static gboolean path_textfield_btn_pressed(GtkWidget *widget, GdkEventButton *event, UiPathTextField *pathtf) {
692 gtk_box_pack_start(GTK_BOX(pathtf->hbox), pathtf->entry, TRUE, TRUE, 0);
693 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox);
694
695 gtk_widget_show(pathtf->entry);
696 gtk_widget_grab_focus(pathtf->entry);
697
698 return TRUE;
699 }
700
701 static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) {
702 for(int i=0;i<nelm;i++) {
703 free(elms[i].name);
704 free(elms[i].path);
705 }
706 free(elms);
707 }
708
709 static void ui_path_textfield_destroy(GtkWidget *object, UiPathTextField *pathtf) {
710 free(pathtf->current_path);
711 g_object_unref(pathtf->entry);
712 free(pathtf);
713 }
714
715 static void ui_path_textfield_activate(GtkWidget *entry, UiPathTextField *pathtf) {
716 const gchar *text = gtk_entry_get_text(GTK_ENTRY(pathtf->entry));
717 if(strlen(text) == 0) {
718 return;
719 }
720
721 UiObject *obj = pathtf->obj;
722
723 if(ui_pathtextfield_update(pathtf, text)) {
724 return;
725 }
726
727 if(pathtf->onactivate) {
728 UiEvent evt;
729 evt.obj = obj;
730 evt.window = obj->window;
731 evt.document = obj->ctx->document;
732 evt.eventdata = (char*)text;
733 evt.intval = -1;
734 pathtf->onactivate(&evt, pathtf->onactivatedata);
735 }
736 }
737
738 static gboolean ui_path_textfield_key_press(GtkWidget *self, GdkEventKey *event, UiPathTextField *pathtf) {
739 if (event->keyval == GDK_KEY_Escape) {
740 // reset GtkEntry value
741 gtk_entry_set_text(GTK_ENTRY(self), pathtf->current_path);
742 const gchar *text = gtk_entry_get_text(GTK_ENTRY(self));
743 ui_pathtextfield_update(pathtf, text);
744 return TRUE;
745 }
746 return FALSE;
747 }
748
749 static GtkWidget* create_path_button_box() {
750 GtkWidget *bb = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
751 gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_EXPAND); // linked style
752 gtk_box_set_homogeneous(GTK_BOX(bb), FALSE);
753 gtk_box_set_spacing(GTK_BOX(bb), 0);
754 return bb;
755 }
756
757 UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) {
758 UiObject* current = uic_current_obj(obj);
759
760 UiPathTextField *pathtf = malloc(sizeof(UiPathTextField));
761 memset(pathtf, 0, sizeof(UiPathTextField));
762 pathtf->obj = obj;
763 pathtf->getpathelm = args.getpathelm;
764 pathtf->getpathelmdata = args.getpathelmdata;
765 pathtf->onactivate = args.onactivate;
766 pathtf->onactivatedata = args.onactivatedata;
767 pathtf->ondragcomplete = args.ondragcomplete;
768 pathtf->ondragcompletedata = args.ondragcompletedata;
769 pathtf->ondragstart = args.ondragstart;
770 pathtf->ondragstartdata = args.ondragstartdata;
771 pathtf->ondrop = args.ondrop;
772 pathtf->ondropdata = args.ondropsdata;
773
774 if(!pathtf->getpathelm) {
775 pathtf->getpathelm = default_pathelm_func;
776 pathtf->getpathelmdata = NULL;
777 }
778
779 // top level container for the path textfield is a GtkEventBox
780 // the event box is needed to handle background button presses
781 GtkWidget *eventbox = gtk_event_box_new();
782 g_signal_connect(
783 eventbox,
784 "button-press-event",
785 G_CALLBACK(path_textfield_btn_pressed),
786 pathtf);
787 g_signal_connect(
788 eventbox,
789 "destroy",
790 G_CALLBACK(ui_path_textfield_destroy),
791 pathtf);
792
793 UI_APPLY_LAYOUT1(current, args);
794 current->container->add(current->container, eventbox, FALSE);
795
796 // hbox as parent for the GtkEntry and GtkButtonBox
797 GtkWidget *hbox = ui_gtk_hbox_new(0);
798 pathtf->hbox = hbox;
799 gtk_container_add(GTK_CONTAINER(eventbox), hbox);
800 gtk_widget_set_name(hbox, "path-textfield-box");
801
802 // create GtkEntry, that is also visible by default (with input yet)
803 pathtf->entry = gtk_entry_new();
804 g_object_ref(G_OBJECT(pathtf->entry));
805 gtk_box_pack_start(GTK_BOX(hbox), pathtf->entry, TRUE, TRUE, 0);
806
807 g_signal_connect(
808 pathtf->entry,
809 "activate",
810 G_CALLBACK(ui_path_textfield_activate),
811 pathtf);
812 g_signal_connect(
813 pathtf->entry,
814 "key-press-event",
815 G_CALLBACK(ui_path_textfield_key_press),
816 pathtf);
817
818 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING);
819 if (var) {
820 UiString* value = (UiString*)var->value;
821 value->obj = pathtf;
822 value->get = ui_path_textfield_get;
823 value->set = ui_path_textfield_set;
824
825 if(value->value.ptr) {
826 char *str = strdup(value->value.ptr);
827 ui_string_set(value, str);
828 free(str);
829 }
830 }
831
832 return hbox;
833 }
834
835 void ui_path_button_clicked(GtkWidget *widget, UiEventData *event) {
836 UiPathElm *elm = event->customdata;
837 cxmutstr path = cx_strdup(cx_strn(elm->path, elm->path_len));
838 UiEvent evt;
839 evt.obj = event->obj;
840 evt.window = evt.obj->window;
841 evt.document = evt.obj->ctx->document;
842 evt.eventdata = elm->path;
843 evt.intval = event->value;
844 event->callback(&evt, event->userdata);
845 free(path.ptr);
846 }
847
848 int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path) {
849 size_t full_path_len = strlen(full_path);
850
851 size_t nelm = 0;
852 UiPathElm* path_elm = pathtf->getpathelm(full_path, full_path_len, &nelm, pathtf->getpathelmdata);
853 if (!path_elm) {
854 return 1;
855 }
856
857 free(pathtf->current_path);
858 pathtf->current_path = strdup(full_path);
859
860 ui_pathelm_destroy(pathtf->current_pathelms, pathtf->current_nelm);
861 pathtf->current_pathelms = path_elm;
862 pathtf->current_nelm = nelm;
863
864 GtkWidget *buttonbox = create_path_button_box();
865 pathtf->buttonbox = buttonbox;
866
867 // switch from entry to buttonbox
868 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->entry);
869 gtk_box_pack_start(GTK_BOX(pathtf->hbox), buttonbox, FALSE, FALSE, 0);
870
871 for (int i=0;i<nelm;i++) {
872 UiPathElm *elm = &path_elm[i];
873
874 cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len));
875 GtkWidget *button = gtk_button_new_with_label(name.ptr);
876 free(name.ptr);
877
878 if(pathtf->onactivate) {
879 UiEventData *eventdata = malloc(sizeof(UiEventData));
880 eventdata->callback = pathtf->onactivate;
881 eventdata->userdata = pathtf->onactivatedata;
882 eventdata->obj = pathtf->obj;
883 eventdata->customdata = elm;
884 eventdata->value = i;
885
886 g_signal_connect(
887 button,
888 "clicked",
889 G_CALLBACK(ui_path_button_clicked),
890 eventdata);
891
892 g_signal_connect(
893 button,
894 "destroy",
895 G_CALLBACK(ui_destroy_userdata),
896 eventdata);
897 }
898
899 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
900 }
901
902 gtk_widget_show_all(buttonbox);
903
904 return 0;
905 }
906
907 char* ui_path_textfield_get(UiString *str) {
908 if(str->value.ptr) {
909 str->value.free(str->value.ptr);
910 }
911 UiPathTextField *tf = str->obj;
912 str->value.ptr = g_strdup(gtk_entry_get_text(GTK_ENTRY(tf->entry)));
913 str->value.free = (ui_freefunc)g_free;
914 return str->value.ptr;
915 }
916
917 void ui_path_textfield_set(UiString *str, const char *value) {
918 UiPathTextField *tf = str->obj;
919 gtk_entry_set_text(GTK_ENTRY(tf->entry), value);
920 ui_pathtextfield_update(tf, value);
921 if(str->value.ptr) {
922 str->value.free(str->value.ptr);
923 str->value.ptr = NULL;
924 str->value.free = NULL;
925 }
926 }

mercurial