mirror of
https://github.com/altlinux/admc.git
synced 2025-03-20 02:50:07 +03:00
switch to qt
This commit is contained in:
parent
33e6b5ad4c
commit
192cd370d6
5
.gitignore
vendored
5
.gitignore
vendored
@ -1 +1,6 @@
|
||||
obj/*
|
||||
moc/*
|
||||
Makefile
|
||||
adtool
|
||||
.qmake.stash
|
||||
*.pro.user
|
||||
|
31
Makefile
31
Makefile
@ -1,31 +0,0 @@
|
||||
|
||||
EXE := adtool
|
||||
SRCDIR := src
|
||||
OBJDIR := obj
|
||||
|
||||
SOURCES += $(shell find $(SRCDIR)/ -name '*.c')
|
||||
OBJS := $(addprefix $(OBJDIR)/, $(addsuffix .o, $(basename $(notdir $(SOURCES)))))
|
||||
|
||||
CFLAGS += -Isrc/libs/
|
||||
CFLAGS += -g -Wall -Wformat
|
||||
CFLAGS += `pkg-config --cflags gtk+-3.0`
|
||||
CFLAGS += -rdynamic
|
||||
LIBS += `pkg-config --libs gtk+-3.0`
|
||||
LIBS += -lldap -llber -lresolv -lgsasl
|
||||
|
||||
$(shell mkdir -p $(OBJDIR))
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/libs/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
all: $(EXE)
|
||||
@echo Build complete
|
||||
|
||||
$(EXE): $(OBJS)
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(EXE) $(OBJS)
|
@ -7,5 +7,5 @@ Requirements:
|
||||
* `libkrb5-devel`
|
||||
* `libgsasl-devel`
|
||||
|
||||
|
||||
Run as `./adtool fake` to load fake entries and test without LDAP connection.
|
||||
|
||||
|
20
adtool.pro
Normal file
20
adtool.pro
Normal file
@ -0,0 +1,20 @@
|
||||
QT += core gui widgets
|
||||
CONFIG += c++11
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
LIBS += -lldap -llber -lresolv -lgsasl
|
||||
|
||||
SOURCES = src/*.cpp
|
||||
HEADERS = src/*.h
|
||||
|
||||
SOURCES += src/active_directory.c
|
||||
|
||||
DESTDIR = .
|
||||
OBJECTS_DIR = obj
|
||||
UI_DIR = src/ui
|
||||
MOC_DIR = moc
|
||||
|
||||
FORMS += \
|
||||
data/ui/mainwindow.ui
|
||||
|
||||
|
@ -1,490 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<object class="GtkTreeStore" id="attributes_model">
|
||||
<columns>
|
||||
<!-- column-name Name -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Value -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeStore" id="containers_model">
|
||||
<columns>
|
||||
<!-- column-name containers_model_column_DN -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name containers_model_column_Name -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="containers_model_filter">
|
||||
<property name="child_model">containers_model</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="contents_model">
|
||||
<columns>
|
||||
<!-- column-name DN -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Name -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Type -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Description -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="contents_model_filter">
|
||||
<property name="child_model">contents_model</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="width_request">900</property>
|
||||
<property name="height_request">600</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkMenuBar" id="menu_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menu_bar_file">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="menu_bar_file_new">
|
||||
<property name="label">gtk-new</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menu_bar_file_new_user">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">User</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="menu_bar_file_new_user_func" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-save-as</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menu_bar_edit">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Edit</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-cut</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-copy</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-paste</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menu_bar_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_View</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkCheckMenuItem" id="advanced_view_toggle">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Advanced View</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="toggled" handler="advanced_view_toggled_func" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menu_bar_help">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label">gtk-about</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="outer_paned">
|
||||
<property name="width_request">200</property>
|
||||
<property name="height_request">500</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="containers_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">containers_model_filter</property>
|
||||
<signal name="row-collapsed" handler="containers_row_collapsed_func" swapped="no"/>
|
||||
<signal name="row-expanded" handler="containers_row_expanded_func" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection">
|
||||
<signal name="changed" handler="containers_selection_changed_func" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="inner_paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="contents_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">contents_model_filter</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection">
|
||||
<signal name="changed" handler="contents_selection_changed_func" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Category</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Description</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">DN</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="attributes_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">attributes_model</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Value</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText">
|
||||
<property name="editable">True</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkDialog" id="new_user_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button1">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button2">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="new_user_dialog_name_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="input_purpose">name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-5">button1</action-widget>
|
||||
<action-widget response="-6">button2</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
161
data/ui/mainwindow.ui
Normal file
161
data/ui/mainwindow.ui
Normal file
@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1307</width>
|
||||
<height>637</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1301</width>
|
||||
<height>591</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="ContainersView" name="containers_view">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ContentsView" name="contents_view">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="AttributesView" name="attributes_view">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1307</width>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuhello">
|
||||
<property name="title">
|
||||
<string>New</string>
|
||||
</property>
|
||||
<addaction name="actionWhat"/>
|
||||
<addaction name="actionSecond"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEdit">
|
||||
<property name="title">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
<addaction name="actionSomething"/>
|
||||
<addaction name="actionHere"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="menubar_view_advancedView"/>
|
||||
</widget>
|
||||
<addaction name="menuhello"/>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menuView"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionWhat">
|
||||
<property name="text">
|
||||
<string>What</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSecond">
|
||||
<property name="text">
|
||||
<string>Second</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSomething">
|
||||
<property name="text">
|
||||
<string>Something</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHere">
|
||||
<property name="text">
|
||||
<string>Here</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menubar_view_advancedView">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Advanced view</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ContainersView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>src/containers_view.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ContentsView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>src/contents_view.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AttributesView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>src/attributes_view.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
37
src/ad_filter.cpp
Normal file
37
src/ad_filter.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
#include "ad_filter.h"
|
||||
#include "ad_model.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
AdFilter::AdFilter(const QAction * const advanced_view_action, bool only_show_containers) {
|
||||
this->only_show_containers = only_show_containers;
|
||||
|
||||
// On advanced view toggle, copy advanced view flag and invalidate filter
|
||||
connect(advanced_view_action, &QAction::toggled,
|
||||
[this](bool checked)
|
||||
{
|
||||
this->advanced_view = checked;
|
||||
this->invalidateFilter();
|
||||
});
|
||||
}
|
||||
|
||||
bool AdFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
|
||||
auto index = sourceModel()->index(source_row, 0, source_parent);
|
||||
|
||||
// Hide advanced view only entries if advanced view is OFF
|
||||
auto advanced_view_only = index.data(AdModel::Roles::AdvancedViewOnly).toBool();
|
||||
if (advanced_view_only && !this->advanced_view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (only_show_containers) {
|
||||
// Hide non-containers
|
||||
auto is_container = index.data(AdModel::Roles::IsContainer).toBool();
|
||||
if (!is_container) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
22
src/ad_filter.h
Normal file
22
src/ad_filter.h
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class QModelIndex;
|
||||
class QAction;
|
||||
class AdModel;
|
||||
|
||||
// TODO: only allow AdModel source models
|
||||
// Filter out advanced entries when advanced view is off
|
||||
// Connected to advanced view toggle in menubar
|
||||
class AdFilter : public QSortFilterProxyModel {
|
||||
public:
|
||||
explicit AdFilter(const QAction * const advanced_view_action, bool only_show_containers = false);
|
||||
|
||||
private:
|
||||
bool advanced_view = false;
|
||||
bool only_show_containers;
|
||||
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
};
|
183
src/ad_interface.cpp
Normal file
183
src/ad_interface.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
|
||||
#include "ad_interface.h"
|
||||
#include "constants.h"
|
||||
|
||||
extern "C" {
|
||||
#include "active_directory.h"
|
||||
}
|
||||
|
||||
// TODO: replace C active_directory.h with C++ version
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// FAKE STUFF
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
bool FAKE_AD = false;
|
||||
|
||||
QMap<QString, QList<QString>> fake_children;
|
||||
QMap<QString, QMap<QString, QList<QString>>> fake_attributes;
|
||||
|
||||
void fake_ad_init() {
|
||||
fake_children[HEAD_DN] = {
|
||||
QString("CN=A,") + HEAD_DN,
|
||||
QString("CN=B,") + HEAD_DN,
|
||||
QString("CN=C,") + HEAD_DN,
|
||||
QString("CN=D,") + HEAD_DN,
|
||||
};
|
||||
|
||||
fake_attributes[HEAD_DN] = {
|
||||
{"name", {"domain"}},
|
||||
{"objectClass", {"container"}},
|
||||
{"objectCategory", {"CN=Container,CN=Schema,CN=Configuration"}},
|
||||
{"showInAdvancedViewOnly", {"FALSE"}},
|
||||
};
|
||||
|
||||
fake_attributes[QString("CN=A,") + HEAD_DN] = {
|
||||
{"name", {"A"}},
|
||||
{"objectClass", {"container"}},
|
||||
{"objectCategory", {"CN=Container,CN=Schema,CN=Configuration"}},
|
||||
{"showInAdvancedViewOnly", {"FALSE"}},
|
||||
{"description", {"A's description"}},
|
||||
};
|
||||
|
||||
fake_attributes[QString("CN=B,") + HEAD_DN] = {
|
||||
{"name", {"B"}},
|
||||
{"objectClass", {"container"}},
|
||||
{"objectCategory", {"CN=Container,CN=Schema,CN=Configuration"}},
|
||||
{"showInAdvancedViewOnly", {"FALSE"}},
|
||||
{"description", {"B's description"}},
|
||||
};
|
||||
|
||||
fake_attributes[QString("CN=C,") + HEAD_DN] = {
|
||||
{"name", {"C"}},
|
||||
{"objectClass", {"container"}},
|
||||
{"objectCategory", {"CN=Container,CN=Schema,CN=Configuration"}},
|
||||
{"showInAdvancedViewOnly", {"FALSE"}},
|
||||
};
|
||||
|
||||
fake_attributes[QString("CN=D,") + HEAD_DN] = {
|
||||
{"name", {"D"}},
|
||||
{"objectClass", {"container"}},
|
||||
{"objectCategory", {"CN=Container,CN=Schema,CN=Configuration"}},
|
||||
{"showInAdvancedViewOnly", {"TRUE"}},
|
||||
};
|
||||
|
||||
fake_children[QString("CN=A,") + HEAD_DN] = {
|
||||
QString("CN=B's child,CN=B,") + HEAD_DN
|
||||
};
|
||||
fake_attributes[QString("CN=B's child,CN=B,") + HEAD_DN] = {
|
||||
{"name", {"B's child"}},
|
||||
{"objectClass", {"person"}},
|
||||
{"objectCategory", {"CN=Person,CN=Schema,CN=Configuration"}},
|
||||
{"showInAdvancedViewOnly", {"FALSE"}},
|
||||
};
|
||||
}
|
||||
|
||||
QList<QString> fake_load_children(QString &dn) {
|
||||
if (!fake_children.contains(dn)) {
|
||||
// NOTE: ok to have empty children for leaves
|
||||
fake_children[dn] = QList<QString>();
|
||||
}
|
||||
|
||||
return fake_children[dn];
|
||||
}
|
||||
|
||||
QMap<QString, QList<QString>> fake_load_attributes(QString &dn) {
|
||||
if (!fake_attributes.contains(dn)) {
|
||||
printf("load_attributes failed for %s, loading empty attributes\n", qPrintable(dn));
|
||||
fake_attributes[dn] = QMap<QString, QList<QString>>();
|
||||
}
|
||||
|
||||
return fake_attributes[dn];
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// REAL STUFF
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
|
||||
bool ad_interface_login() {
|
||||
if (FAKE_AD) {
|
||||
fake_ad_init();
|
||||
return true;
|
||||
}
|
||||
|
||||
LDAP* ldap_connection = ad_login();
|
||||
if (ldap_connection == NULL) {
|
||||
printf("ad_login error: %s\n", ad_get_error());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QString> load_children(QString &dn) {
|
||||
if (FAKE_AD) {
|
||||
return fake_load_children(dn);
|
||||
}
|
||||
|
||||
char **children_raw = ad_list(dn.toLatin1().data());
|
||||
|
||||
// TODO: error check
|
||||
|
||||
if (children_raw != NULL) {
|
||||
auto children = QList<QString>();
|
||||
|
||||
for (int i = 0; children_raw[i] != NULL; i++) {
|
||||
auto child = QString(children_raw[i]);
|
||||
children.push_back(child);
|
||||
}
|
||||
|
||||
for (int i = 0; children_raw[i] != NULL; i++) {
|
||||
free(children_raw[i]);
|
||||
}
|
||||
free(children_raw);
|
||||
|
||||
return children;
|
||||
} else {
|
||||
return QList<QString>();
|
||||
}
|
||||
}
|
||||
|
||||
QMap<QString, QList<QString>> load_attributes(QString &dn) {
|
||||
if (FAKE_AD) {
|
||||
return fake_load_attributes(dn);
|
||||
}
|
||||
|
||||
// TODO: save original attributes ordering and load it like that into model
|
||||
|
||||
char** attributes_raw = ad_get_attribute(dn.toLatin1().data(), NULL);
|
||||
|
||||
// TODO: error check
|
||||
|
||||
if (attributes_raw != NULL) {
|
||||
auto attributes = QMap<QString, QList<QString>>();
|
||||
|
||||
// Load attributes map
|
||||
// attributes_raw is in the form of:
|
||||
// char** array of {key, value, value, key, value ...}
|
||||
// transform it into:
|
||||
// map of {key => {value, value ...}, key => {value, value ...} ...}
|
||||
for (int i = 0; attributes_raw[i + 2] != NULL; i += 2) {
|
||||
auto attribute = QString(attributes_raw[i]);
|
||||
auto value = QString(attributes_raw[i + 1]);
|
||||
|
||||
// Make values list if doesn't exist yet
|
||||
if (!attributes.contains(attribute)) {
|
||||
attributes[attribute] = QList<QString>();
|
||||
}
|
||||
|
||||
attributes[attribute].push_back(value);
|
||||
}
|
||||
|
||||
// Free attributes_raw
|
||||
for (int i = 0; attributes_raw[i] != NULL; i++) {
|
||||
free(attributes_raw[i]);
|
||||
}
|
||||
free(attributes_raw);
|
||||
|
||||
return attributes;
|
||||
} else {
|
||||
return QMap<QString, QList<QString>>();
|
||||
}
|
||||
}
|
15
src/ad_interface.h
Normal file
15
src/ad_interface.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
// Interface functions to convert from raw char** active directory returns to Qt containers
|
||||
// Also can load fake data if program is run with "fake" option
|
||||
|
||||
extern bool FAKE_AD;
|
||||
|
||||
bool ad_interface_login();
|
||||
QList<QString> load_children(QString &dn);
|
||||
QMap<QString, QList<QString>> load_attributes(QString &dn);
|
134
src/ad_model.cpp
Normal file
134
src/ad_model.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
#include "ad_model.h"
|
||||
#include "ad_interface.h"
|
||||
#include "constants.h"
|
||||
|
||||
#include <QMap>
|
||||
|
||||
QList<QStandardItem*> make_row(QString dn) {
|
||||
auto attributes = load_attributes(dn);
|
||||
|
||||
// TODO: get rid of "if (x.contains(y))"
|
||||
|
||||
// Name
|
||||
auto name = dn;
|
||||
if (attributes.contains("name")) {
|
||||
name = attributes["name"][0];
|
||||
}
|
||||
|
||||
// Category
|
||||
auto category = dn;
|
||||
if (attributes.contains("objectCategory")) {
|
||||
// NOTE: raw category is given as DN
|
||||
// TODO: convert it completely (turn '-' into ' ')
|
||||
auto category_as_dn = attributes["objectCategory"][0];
|
||||
auto equals_index = category_as_dn.indexOf('=');
|
||||
auto comma_index = category_as_dn.indexOf(',');
|
||||
auto segment_length = comma_index - equals_index;
|
||||
// TODO: check what happens if equals is negative
|
||||
category = category_as_dn.mid(equals_index + 1, segment_length);
|
||||
}
|
||||
|
||||
// Description
|
||||
QString description = "none";
|
||||
if (attributes.contains("description")) {
|
||||
description = attributes["description"][0];
|
||||
}
|
||||
|
||||
// showInAdvancedViewOnly
|
||||
bool advanced_view = false;
|
||||
if (attributes.contains("showInAdvancedViewOnly")) {
|
||||
auto advanced_view_str = attributes["showInAdvancedViewOnly"][0];
|
||||
|
||||
if (advanced_view_str == "TRUE") {
|
||||
advanced_view = true;
|
||||
}
|
||||
}
|
||||
|
||||
// is container
|
||||
bool is_container = false;
|
||||
if (attributes.contains("objectClass")) {
|
||||
auto objectClasses = attributes["objectClass"];
|
||||
|
||||
QList<QString> container_objectClasses = {"container", "organizationalUnit", "builtinDomain", "domain"};
|
||||
for (auto e : container_objectClasses) {
|
||||
if (objectClasses.contains(e)) {
|
||||
is_container = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QStandardItem*> row = {
|
||||
new QStandardItem(name),
|
||||
new QStandardItem(category),
|
||||
new QStandardItem(description),
|
||||
};
|
||||
|
||||
// NOTE: store special invisible attributes in Roles of first item
|
||||
// TODO: shouldn't store these in roles
|
||||
row[0]->setData(dn, AdModel::Roles::DN);
|
||||
row[0]->setData(advanced_view, AdModel::Roles::AdvancedViewOnly);
|
||||
row[0]->setData(true, AdModel::Roles::CanFetch);
|
||||
row[0]->setData(is_container, AdModel::Roles::IsContainer);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
AdModel::AdModel(): QStandardItemModel(0, Column::COUNT) {
|
||||
this->setHorizontalHeaderItem(Column::Name, new QStandardItem("Name"));
|
||||
this->setHorizontalHeaderItem(Column::Category, new QStandardItem("Category"));
|
||||
this->setHorizontalHeaderItem(Column::Description, new QStandardItem("Description"));
|
||||
|
||||
// Load head
|
||||
auto invis_root = this->invisibleRootItem();
|
||||
auto head_row = make_row(HEAD_DN);
|
||||
invis_root->appendRow(head_row);
|
||||
}
|
||||
|
||||
bool AdModel::canFetchMore(const QModelIndex &parent) const {
|
||||
if (!parent.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dn = parent.data(Roles::DN).toString();
|
||||
// printf("canFetchMore: %s\n", qPrintable(dn));
|
||||
|
||||
auto can_fetch = parent.data(AdModel::Roles::CanFetch).toBool();
|
||||
|
||||
return can_fetch;
|
||||
}
|
||||
|
||||
void AdModel::fetchMore(const QModelIndex &parent) {
|
||||
if (!parent.isValid() || !this->canFetchMore(parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dn = parent.data(Roles::DN).toString();
|
||||
|
||||
auto parent_item = this->itemFromIndex(parent);
|
||||
|
||||
// Add children
|
||||
auto children = load_children(dn);
|
||||
|
||||
for (auto child : children) {
|
||||
auto child_row = make_row(child);
|
||||
|
||||
parent_item->appendRow(child_row);
|
||||
}
|
||||
|
||||
// Unset CanFetch flag
|
||||
parent_item->setData(false, AdModel::Roles::CanFetch);
|
||||
|
||||
// printf("fetchMore: %s\n", qPrintable(dn));
|
||||
}
|
||||
|
||||
// Override this so that unexpanded and unfetched items show the expander even though they technically don't have any children loaded
|
||||
// NOTE: expander is show if hasChildren returns true
|
||||
bool AdModel::hasChildren(const QModelIndex &parent = QModelIndex()) const {
|
||||
if (this->canFetchMore(parent)) {
|
||||
return true;
|
||||
} else {
|
||||
return QStandardItemModel::hasChildren(parent);
|
||||
}
|
||||
}
|
31
src/ad_model.h
Normal file
31
src/ad_model.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
class AdModel: public QStandardItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Column {
|
||||
Name,
|
||||
Category,
|
||||
Description,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
explicit AdModel();
|
||||
|
||||
enum Roles {
|
||||
DN = Qt::UserRole + 1,
|
||||
AdvancedViewOnly = Qt::UserRole + 2,
|
||||
CanFetch = Qt::UserRole + 3,
|
||||
IsContainer = Qt::UserRole + 4,
|
||||
};
|
||||
|
||||
bool canFetchMore(const QModelIndex &parent) const;
|
||||
void fetchMore(const QModelIndex &parent);
|
||||
bool hasChildren(const QModelIndex &parent) const override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
18
src/attributes_model.cpp
Normal file
18
src/attributes_model.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
#include "attributes_model.h"
|
||||
|
||||
AttributesModel::AttributesModel(): QStandardItemModel(0, Column::COUNT) {
|
||||
this->setHorizontalHeaderItem(Column::Name, new QStandardItem("Name"));
|
||||
this->setHorizontalHeaderItem(Column::Value, new QStandardItem("Value"));
|
||||
}
|
||||
|
||||
// This will be called when an attribute value is edited
|
||||
bool AttributesModel::setData(const QModelIndex &index, const QVariant &value, int role) {
|
||||
QStandardItemModel::setData(index, value, role);
|
||||
|
||||
// TODO: attribute edit can fail for many reasons, handle it
|
||||
|
||||
// TODO: reload attributes in ad model
|
||||
|
||||
return true;
|
||||
}
|
21
src/attributes_model.h
Normal file
21
src/attributes_model.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
class AttributesModel: public QStandardItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Column {
|
||||
Name,
|
||||
Value,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
explicit AttributesModel();
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||
|
||||
private:
|
||||
|
||||
};
|
@ -1,99 +0,0 @@
|
||||
|
||||
#include "attributes_view.h"
|
||||
|
||||
#include "constants.h"
|
||||
#include "utils.h"
|
||||
#include "entry.h"
|
||||
|
||||
#include "stb_ds.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
// Lists attributes of target entry
|
||||
// Attributes target entry is selected in contents view
|
||||
|
||||
enum {
|
||||
ATTRIBUTES_COLUMN_NAME,
|
||||
ATTRIBUTES_COLUMN_VALUE,
|
||||
ATTRIBUTES_COLUMN_COUNT
|
||||
};
|
||||
|
||||
GtkTreeView* attributes_view = NULL;
|
||||
char* attributes_target = NULL;
|
||||
|
||||
// NOTE: currently doesn't work for multi-valued attributes, add that capability when/if it's needed
|
||||
void attributes_value_edited_func(
|
||||
GtkCellRendererText* cell,
|
||||
gchar* path_string,
|
||||
gchar* new_text,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTreeModel* model = gtk_tree_view_get_model(attributes_view);
|
||||
|
||||
GtkTreeIter iter;
|
||||
gtk_tree_model_get_iter_from_string(model, &iter, path_string);
|
||||
|
||||
char* attribute_name;
|
||||
gtk_tree_model_get(model, &iter, ATTRIBUTES_COLUMN_NAME, &attribute_name, -1);
|
||||
|
||||
char* old_value;
|
||||
gtk_tree_model_get(model, &iter, ATTRIBUTES_COLUMN_VALUE, &old_value, -1);
|
||||
|
||||
entry* e = get_entry(attributes_target);
|
||||
bool edit_success = entry_edit_value(e, attribute_name, new_text);
|
||||
|
||||
if (edit_success) {
|
||||
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, ATTRIBUTES_COLUMN_VALUE, new_text, -1);
|
||||
|
||||
printf("attribute %s changed: %s > %s\n", attribute_name, old_value, new_text);
|
||||
} else {
|
||||
// TODO: display error
|
||||
}
|
||||
|
||||
free(attribute_name);
|
||||
free(old_value);
|
||||
}
|
||||
|
||||
void attributes_change_target(const char* new_target_dn) {
|
||||
if (attributes_target != NULL) {
|
||||
free(attributes_target);
|
||||
}
|
||||
attributes_target = strdup(new_target_dn);
|
||||
}
|
||||
|
||||
// NOTE: model is set when an entry is selected in contents pane
|
||||
void attributes_populate_model() {
|
||||
// Populate model
|
||||
// List all key->value pairs in order
|
||||
GtkTreeStore* model = GTK_TREE_STORE(gtk_tree_view_get_model(attributes_view));
|
||||
gtk_tree_store_clear(model);
|
||||
|
||||
entry* e = get_entry(attributes_target);
|
||||
|
||||
// Target is invalid
|
||||
// NOTE: this is valid behavior and can occur when target entry is deleted for example
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
STR_ARRAY attribute_keys = e->attribute_keys;
|
||||
for (int i = 0; i < arrlen(attribute_keys); i++) {
|
||||
char* key = attribute_keys[i];
|
||||
STR_ARRAY values = shget(e->attributes, key);
|
||||
|
||||
for (int j = 0; j < arrlen(values); j++) {
|
||||
GtkTreeIter this_node;
|
||||
gtk_tree_store_append(model, &this_node, NULL);
|
||||
gtk_tree_store_set(model, &this_node, ATTRIBUTES_COLUMN_NAME, key, -1);
|
||||
gtk_tree_store_set(model, &this_node, ATTRIBUTES_COLUMN_VALUE, values[j], -1);
|
||||
|
||||
// TODO: handle this, can't draw this text properly
|
||||
if (streql(key, "objectGUID")) {
|
||||
gtk_tree_store_set(model, &this_node, ATTRIBUTES_COLUMN_VALUE, "!!!can't render this for now", -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void attributes_init(GtkBuilder* builder) {
|
||||
attributes_view = GTK_TREE_VIEW(gtk_builder_get_object_CHECKED(builder, "attributes_view"));
|
||||
}
|
42
src/attributes_view.cpp
Normal file
42
src/attributes_view.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
#include "attributes_view.h"
|
||||
#include "ad_model.h"
|
||||
#include "ad_interface.h"
|
||||
|
||||
// TODO: on attribute edit, update entry in ad model
|
||||
// since attributes affect model's contents
|
||||
|
||||
void AttributesView::set_target_from_selection(const QItemSelection &selected, const QItemSelection &) {
|
||||
// Clear model of previous root
|
||||
// TODO: get rid of cast
|
||||
auto model = qobject_cast<QStandardItemModel *>(this->model());
|
||||
if (model != nullptr) {
|
||||
model->clear();
|
||||
model->setHorizontalHeaderItem(Column::Name, new QStandardItem("Name"));
|
||||
model->setHorizontalHeaderItem(Column::Value, new QStandardItem("Value"));
|
||||
}
|
||||
|
||||
// Convert selection to dn
|
||||
auto indexes = selected.indexes();
|
||||
if (indexes.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto index = indexes[0];
|
||||
this->root_dn = index.data(AdModel::Roles::DN).toString();
|
||||
|
||||
// Populate model with attributes of new root
|
||||
auto attributes = load_attributes(this->root_dn);
|
||||
for (auto attribute : attributes.keys()) {
|
||||
auto values = attributes[attribute];
|
||||
|
||||
for (auto value : values) {
|
||||
auto name_item = new QStandardItem(attribute);
|
||||
auto value_item = new QStandardItem(value);
|
||||
|
||||
name_item->setEditable(false);
|
||||
|
||||
model->appendRow({name_item, value_item});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,25 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <QTreeView>
|
||||
|
||||
void attributes_init(GtkBuilder* builder);
|
||||
void attributes_change_target(const char* new_target_dn);
|
||||
void attributes_populate_model();
|
||||
// Shows names and values of attributes of the entry selected in contents view
|
||||
class AttributesView : public QTreeView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using QTreeView::QTreeView;
|
||||
|
||||
public slots:
|
||||
void set_target_from_selection(const QItemSelection &selected, const QItemSelection &deselected);
|
||||
|
||||
private:
|
||||
enum Column {
|
||||
Name,
|
||||
Value,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
QString root_dn;
|
||||
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define DN_LENGTH_MAX 1000
|
||||
#define HEAD_DN "DC=domain,DC=alt"
|
||||
#define HEAD_DN "DC=domain,DC=alt"
|
@ -1,143 +0,0 @@
|
||||
|
||||
#include "containers_view.h"
|
||||
|
||||
#include "contents_view.h"
|
||||
#include "constants.h"
|
||||
#include "utils.h"
|
||||
#include "entry.h"
|
||||
#include "menu_bar.h"
|
||||
|
||||
#include "stb_ds.h"
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
// Shows a tree of LDAP containers
|
||||
|
||||
enum {
|
||||
CONTAINERS_COLUMN_DN,
|
||||
CONTAINERS_COLUMN_NAME,
|
||||
};
|
||||
|
||||
GtkTreeView* containers_view = NULL;
|
||||
|
||||
void containers_refilter() {
|
||||
GtkTreeModelFilter* model_filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(containers_view));
|
||||
gtk_tree_model_filter_refilter(model_filter);
|
||||
}
|
||||
|
||||
void containers_row_expanded_func(
|
||||
GtkTreeView *tree_view,
|
||||
GtkTreeIter *iter,
|
||||
GtkTreePath *path,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTreeModel* model = gtk_tree_view_get_model(tree_view);
|
||||
|
||||
char* dn;
|
||||
gtk_tree_model_get(model, iter, CONTAINERS_COLUMN_DN, &dn, -1);
|
||||
|
||||
printf("containers_row_expanded %s\n", dn);
|
||||
|
||||
free(dn);
|
||||
}
|
||||
|
||||
void containers_row_collapsed_func(
|
||||
GtkTreeView *tree_view,
|
||||
GtkTreeIter *iter,
|
||||
GtkTreePath *path,
|
||||
gpointer user_data)
|
||||
{
|
||||
// printf("containers_row_expanded %s\n", dn);
|
||||
}
|
||||
|
||||
void containers_selection_changed_func(GtkTreeSelection* selection, gpointer user_data) {
|
||||
// Get selected iter
|
||||
GtkTreeView* view = gtk_tree_selection_get_tree_view(selection);
|
||||
GtkTreeModel* model = gtk_tree_view_get_model(view);
|
||||
GtkTreeIter iter;
|
||||
gboolean any_selected = gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||
if (!any_selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get dn of selected iter
|
||||
char* dn;
|
||||
gtk_tree_model_get(model, &iter, CONTAINERS_COLUMN_DN, &dn, -1);
|
||||
|
||||
// Update contents model
|
||||
contents_change_target(dn);
|
||||
contents_populate_model();
|
||||
|
||||
free(dn);
|
||||
}
|
||||
|
||||
gboolean containers_filter_func(
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
// Filter out entries with "showInAdvancedViewOnly" set to TRUE
|
||||
gboolean visible = TRUE;
|
||||
|
||||
char* dn;
|
||||
gtk_tree_model_get(model, iter, CONTAINERS_COLUMN_DN, &dn, -1);
|
||||
|
||||
if (dn != NULL) {
|
||||
entry* e = get_entry(dn);
|
||||
char* showInAdvancedViewOnly = entry_get_attribute_or_none(e, "showInAdvancedViewOnly");
|
||||
if (!advanced_view_is_on() && streql(showInAdvancedViewOnly, "TRUE")) {
|
||||
visible = FALSE;
|
||||
}
|
||||
|
||||
g_free(dn);
|
||||
}
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
void containers_populate_model_recursive(GtkTreeStore* model, char* node_dn, GtkTreeIter* parent) {
|
||||
// Populate model with name's of entries
|
||||
entry* e = get_entry(node_dn);
|
||||
|
||||
// Skip if entry is not a container
|
||||
if (!entry_is_container(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GtkTreeIter this_node;
|
||||
gtk_tree_store_append(model, &this_node, parent);
|
||||
|
||||
gtk_tree_store_set(model, &this_node, CONTAINERS_COLUMN_DN, e->dn, -1);
|
||||
|
||||
char name[DN_LENGTH_MAX];
|
||||
first_element_in_dn(name, e->dn, DN_LENGTH_MAX);
|
||||
gtk_tree_store_set(model, &this_node, CONTAINERS_COLUMN_NAME, name, -1);
|
||||
|
||||
// Recurse into entry's children
|
||||
for (int i = 0; i < arrlen(e->children); i++) {
|
||||
char* child_dn = e->children[i];
|
||||
|
||||
containers_populate_model_recursive(model, child_dn, &this_node);
|
||||
}
|
||||
}
|
||||
|
||||
void containers_populate_model() {
|
||||
GtkTreeModelFilter* model_filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(containers_view));
|
||||
GtkTreeStore* model = GTK_TREE_STORE(gtk_tree_model_filter_get_model(model_filter));
|
||||
gtk_tree_store_clear(model);
|
||||
|
||||
containers_populate_model_recursive(model, HEAD_DN, NULL);
|
||||
|
||||
gtk_tree_model_filter_refilter(model_filter);
|
||||
}
|
||||
|
||||
void containers_init(GtkBuilder* builder) {
|
||||
containers_view = GTK_TREE_VIEW(gtk_builder_get_object_CHECKED(builder, "containers_view"));
|
||||
|
||||
// Set filter func
|
||||
GtkTreeModelFilter* model_filter = GTK_TREE_MODEL_FILTER((gtk_tree_view_get_model(containers_view)));
|
||||
gtk_tree_model_filter_set_visible_func(model_filter, containers_filter_func, NULL, NULL);
|
||||
|
||||
containers_populate_model();
|
||||
}
|
10
src/containers_view.cpp
Normal file
10
src/containers_view.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
#include "containers_view.h"
|
||||
#include "ad_model.h"
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QItemSelection>
|
||||
|
||||
ContainersView::ContainersView(QWidget *parent) : QTreeView(parent) {
|
||||
|
||||
};
|
@ -1,7 +1,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <QTreeView>
|
||||
|
||||
void containers_init(GtkBuilder* builder);
|
||||
void containers_refilter();
|
||||
void containers_populate_model();
|
||||
// Shows names of AdModel as a tree
|
||||
class ContainersView : public QTreeView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ContainersView(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
|
||||
signals:
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
@ -1,135 +0,0 @@
|
||||
|
||||
#include "contents_view.h"
|
||||
|
||||
#include "attributes_view.h"
|
||||
#include "entry.h"
|
||||
#include "constants.h"
|
||||
#include "menu_bar.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "stb_ds.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
// Shows a list of LDAP entries that children of current target entry
|
||||
// Contents target entry is selected in containers view
|
||||
|
||||
enum {
|
||||
CONTENTS_COLUMN_DN,
|
||||
CONTENTS_COLUMN_NAME,
|
||||
CONTENTS_COLUMN_CATEGORY,
|
||||
CONTENTS_COLUMN_DESCRIPTION,
|
||||
CONTENTS_COLUMN_COUNT
|
||||
};
|
||||
|
||||
GtkTreeView* contents_view = NULL;
|
||||
char* contents_target = NULL;
|
||||
|
||||
void contents_refilter() {
|
||||
GtkTreeModelFilter* model_filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(contents_view));
|
||||
gtk_tree_model_filter_refilter(model_filter);
|
||||
}
|
||||
|
||||
gboolean contents_filter_func(
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
// Filter out entries with "showInAdvancedViewOnly" set to TRUE
|
||||
gboolean visible = TRUE;
|
||||
|
||||
char* dn;
|
||||
gtk_tree_model_get(model, iter, CONTENTS_COLUMN_DN, &dn, -1);
|
||||
if (dn != NULL) {
|
||||
entry* e = get_entry(dn);
|
||||
STR_ARRAY showInAdvancedViewOnly = entry_get_attribute(e, "showInAdvancedViewOnly");
|
||||
if (!advanced_view_is_on() && showInAdvancedViewOnly != NULL && streql(showInAdvancedViewOnly[0], "TRUE")) {
|
||||
visible = FALSE;
|
||||
}
|
||||
|
||||
g_free(dn);
|
||||
}
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
void contents_selection_changed_func(GtkTreeSelection* selection, gpointer user_data) {
|
||||
GtkTreeView* view = gtk_tree_selection_get_tree_view(selection);
|
||||
GtkTreeModel* model = gtk_tree_view_get_model(view);
|
||||
|
||||
GtkTreeIter iter;
|
||||
|
||||
gboolean any_selected = gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||
if (!any_selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get dn of selected iter
|
||||
char* dn;
|
||||
gtk_tree_model_get(model, &iter, CONTENTS_COLUMN_DN, &dn, -1);
|
||||
|
||||
// Update contents model
|
||||
attributes_change_target(dn);
|
||||
attributes_populate_model();
|
||||
|
||||
free(dn);
|
||||
}
|
||||
|
||||
void contents_change_target(const char* new_target_dn) {
|
||||
if (contents_target != NULL) {
|
||||
free(contents_target);
|
||||
}
|
||||
contents_target = strdup(new_target_dn);
|
||||
}
|
||||
|
||||
// NOTE: contents model is repopulated everytime a new container is selected
|
||||
void contents_populate_model() {
|
||||
GtkTreeModelFilter* model_filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(contents_view));
|
||||
GtkListStore* model = GTK_LIST_STORE(gtk_tree_model_filter_get_model(model_filter));
|
||||
gtk_list_store_clear(model);
|
||||
|
||||
if (contents_target == NULL) {
|
||||
printf("ERROR contents_populate_model(): no target\n");
|
||||
return;
|
||||
}
|
||||
|
||||
entry* e = get_entry(contents_target);
|
||||
|
||||
// Target is invalid
|
||||
// NOTE: this is valid behavior and can occur when target entry is deleted for example
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate model
|
||||
for (int i = 0; i < arrlen(e->children); i++) {
|
||||
char* child_dn = e->children[i];
|
||||
entry* child = get_entry(child_dn);
|
||||
|
||||
GtkTreeIter this_node;
|
||||
gtk_list_store_append(model, &this_node);
|
||||
|
||||
char* dn = child->dn;
|
||||
gtk_list_store_set(model, &this_node, CONTENTS_COLUMN_DN, dn, -1);
|
||||
|
||||
char name[DN_LENGTH_MAX];
|
||||
first_element_in_dn(name, dn, DN_LENGTH_MAX);
|
||||
gtk_list_store_set(model, &this_node, CONTENTS_COLUMN_NAME, name, -1);
|
||||
|
||||
char* category_dn = entry_get_attribute_or_none(child, "objectCategory");
|
||||
gtk_list_store_set(model, &this_node, CONTENTS_COLUMN_CATEGORY, category_dn, -1);
|
||||
|
||||
char* description = entry_get_attribute_or_none(child, "description");
|
||||
gtk_list_store_set(model, &this_node, CONTENTS_COLUMN_DESCRIPTION, description, -1);
|
||||
}
|
||||
|
||||
gtk_tree_model_filter_refilter(model_filter);
|
||||
}
|
||||
|
||||
void contents_init(GtkBuilder* builder) {
|
||||
contents_view = GTK_TREE_VIEW(gtk_builder_get_object_CHECKED(builder, "contents_view"));
|
||||
|
||||
// Set filter func
|
||||
GtkTreeModelFilter* model_filter = GTK_TREE_MODEL_FILTER((gtk_tree_view_get_model(contents_view)));
|
||||
gtk_tree_model_filter_set_visible_func(model_filter, contents_filter_func, NULL, NULL);
|
||||
gtk_tree_model_filter_refilter(model_filter);
|
||||
}
|
40
src/contents_view.cpp
Normal file
40
src/contents_view.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
#include "contents_view.h"
|
||||
|
||||
#include <QItemSelection>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
void ContentsView::set_root_index_from_selection(const QItemSelection &selected, const QItemSelection &) {
|
||||
auto indexes = selected.indexes();
|
||||
|
||||
if (indexes.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Map from proxy model of given index to source model of this view (if needed)
|
||||
auto source_index = indexes[0];
|
||||
{
|
||||
auto model = source_index.model();
|
||||
auto proxy_model = qobject_cast<const QSortFilterProxyModel *>(model);
|
||||
if (proxy_model != nullptr) {
|
||||
source_index = proxy_model->mapToSource(source_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Map from source model of this view to proxy model of this view (if needed)
|
||||
auto contents_index = source_index;
|
||||
{
|
||||
auto model = this->model();
|
||||
auto proxy_model = qobject_cast<const QSortFilterProxyModel *>(model);
|
||||
if (proxy_model != nullptr) {
|
||||
contents_index = proxy_model->mapFromSource(contents_index);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->model()->checkIndex(contents_index)) {
|
||||
printf("ContentsView::set_root_index_from_selection received bad index!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
this->setRootIndex(contents_index);
|
||||
}
|
@ -1,9 +1,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <QTreeView>
|
||||
|
||||
void contents_init(GtkBuilder* builder);
|
||||
void contents_change_target(const char* new_target_dn);
|
||||
void contents_populate_model();
|
||||
void contents_refilter();
|
||||
void contents_selection_changed_func(GtkTreeSelection* selection, gpointer user_data);
|
||||
// Shows name, category and description of children of entry selected in containers view
|
||||
class ContentsView : public QTreeView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using QTreeView::QTreeView;
|
||||
|
||||
public slots:
|
||||
void set_root_index_from_selection(const QItemSelection &selected, const QItemSelection &);
|
||||
|
||||
private:
|
||||
|
||||
};
|
409
src/entry.c
409
src/entry.c
@ -1,409 +0,0 @@
|
||||
|
||||
#include "entry.h"
|
||||
|
||||
#include "containers_view.h"
|
||||
#include "contents_view.h"
|
||||
#include "utils.h"
|
||||
#include "constants.h"
|
||||
|
||||
// NOTE: need to define this once in one .c file
|
||||
#define STB_DS_IMPLEMENTATION
|
||||
#include "stb_ds.h"
|
||||
#include "active_directory.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
entries_map* entries;
|
||||
|
||||
entry* get_entry(const char* dn) {
|
||||
return shget(entries, dn);
|
||||
}
|
||||
|
||||
void entry_load(const char* dn) {
|
||||
entry* e = (entry*)malloc(sizeof(entry));
|
||||
|
||||
e->dn = strdup(dn);
|
||||
|
||||
//
|
||||
// Load entry's attributes
|
||||
//
|
||||
e->attributes = NULL;
|
||||
e->attribute_keys = NULL;
|
||||
char** attributes_raw = ad_get_attribute(e->dn, NULL);
|
||||
if (attributes_raw != NULL) {
|
||||
// Init entry's attributes map
|
||||
sh_new_strdup(e->attributes);
|
||||
|
||||
// Load e->attributes
|
||||
// attributes_raw is in the form of:
|
||||
// char** array of [key, value, value, key, value ...]
|
||||
// it's a map of keys to values where keys can map to multiple values
|
||||
// transform it into:
|
||||
// stb map of [key => [value, value ...], key => [value, value ...] ...]
|
||||
for (int i = 0; attributes_raw[i + 2] != NULL; i += 2) {
|
||||
char* key = attributes_raw[i];
|
||||
|
||||
// Get current values of this attribute
|
||||
STR_ARRAY values = shget(e->attributes, key);
|
||||
// Append new value to values list
|
||||
arrput(values, strdup(attributes_raw[i + 1]));
|
||||
// Update values in attributes map
|
||||
shput(e->attributes, key, values);
|
||||
}
|
||||
|
||||
// Load e->attributes_keys, which contains keys in their original ordering
|
||||
char* current_key = NULL;
|
||||
for (int i = 0; attributes_raw[i + 2] != NULL; i += 2) {
|
||||
char* key = attributes_raw[i];
|
||||
|
||||
if (current_key == NULL || !streql(current_key, key)) {
|
||||
// Encountered a new key, save it
|
||||
arrput(e->attribute_keys, strdup(key));
|
||||
current_key = key;
|
||||
}
|
||||
}
|
||||
|
||||
// Free attributes_raw
|
||||
for (int i = 0; attributes_raw[i] != NULL; i++) {
|
||||
free(attributes_raw[i]);
|
||||
}
|
||||
free(attributes_raw);
|
||||
}
|
||||
|
||||
//
|
||||
// Load children list
|
||||
//
|
||||
e->children = NULL;
|
||||
char** children_dns = ad_list(e->dn);
|
||||
if (children_dns != NULL) {
|
||||
for (int i = 0; children_dns[i] != NULL; i++) {
|
||||
char* child_dn = children_dns[i];
|
||||
arrput(e->children, strdup(child_dn));
|
||||
|
||||
// Preload children
|
||||
entry_load(child_dn);
|
||||
}
|
||||
|
||||
free(children_dns);
|
||||
}
|
||||
|
||||
// Add entry to entries map
|
||||
shput(entries, e->dn, e);
|
||||
}
|
||||
|
||||
void entry_init() {
|
||||
// Init map to use strdup for string allocation
|
||||
sh_new_strdup(entries);
|
||||
|
||||
// Load entries recursively
|
||||
entry_load(HEAD_DN);
|
||||
}
|
||||
|
||||
STR_ARRAY entry_get_attribute(entry* e, const char* key) {
|
||||
return shget(e->attributes, key);
|
||||
}
|
||||
|
||||
char* entry_get_attribute_or_none(entry* e, const char* key) {
|
||||
static char* none_str = "none";
|
||||
STR_ARRAY attribute_array = entry_get_attribute(e, key);
|
||||
if (attribute_array != NULL) {
|
||||
return attribute_array[0];
|
||||
} else {
|
||||
return none_str;
|
||||
}
|
||||
}
|
||||
|
||||
bool entry_attribute_exists(entry* e, const char* key, const char* value) {
|
||||
STR_ARRAY values = shget(e->attributes, key);
|
||||
|
||||
if (values == NULL) {
|
||||
return false;
|
||||
} else {
|
||||
for (int i = 0; i < arrlen(values); i++) {
|
||||
if (streql(values[i], value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void entry_delete(entry* e) {
|
||||
int result = ad_object_delete(e->dn);
|
||||
|
||||
if (result == AD_SUCCESS) {
|
||||
// Delete entry from parent's child list
|
||||
// Get parent dn by cutting part before first comma
|
||||
// TODO: get this dn in a better way
|
||||
char* dn = e->dn;
|
||||
const char* comma = strchr(dn, ',');
|
||||
char parent_dn[DN_LENGTH_MAX];
|
||||
strncpy(parent_dn, comma, DN_LENGTH_MAX);
|
||||
|
||||
entry* parent = shget(entries, parent_dn);
|
||||
if (parent != NULL) {
|
||||
// Find child index in children array
|
||||
STR_ARRAY children = parent->children;
|
||||
int delete_i = -1;
|
||||
for (int i = 0; i < arrlen(children); i++) {
|
||||
char* child = children[i];
|
||||
if (streql(child, dn)) {
|
||||
delete_i = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_i != -1) {
|
||||
arrdel(children, delete_i);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Free entry and all it's members
|
||||
//
|
||||
shdel(entries, e->dn);
|
||||
|
||||
free(e->dn);
|
||||
|
||||
// Free attributes
|
||||
for (int i = 0; i < shlen(e->attributes); i++) {
|
||||
STR_ARRAY values = e->attributes[i].value;
|
||||
|
||||
for (int j = 0; j < arrlen(values); j++) {
|
||||
char* attribute = values[j];
|
||||
free(attribute);
|
||||
}
|
||||
|
||||
arrfree(values);
|
||||
}
|
||||
shfree(e->attributes);
|
||||
|
||||
// Free attribute_keys
|
||||
for (int i = 0; i < arrlen(e->attribute_keys); i++) {
|
||||
char* key = e->attribute_keys[i];
|
||||
free(key);
|
||||
}
|
||||
arrfree(e->attribute_keys);
|
||||
|
||||
// Free children
|
||||
for (int i = 0; i < arrlen(e->children); i++) {
|
||||
free(e->children[i]);
|
||||
}
|
||||
arrfree(e->children);
|
||||
|
||||
// Replicate changes in view models
|
||||
containers_populate_model();
|
||||
contents_populate_model();
|
||||
} else {
|
||||
printf("ad_object_delete error: %s\n", ad_get_error());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: probably need to add more objectClasses
|
||||
bool entry_is_container(entry* e) {
|
||||
static const char* container_objectClasses[4] = {"container", "organizationalUnit", "builtinDomain", "domain"};
|
||||
int container_objectClasses_size = sizeof(container_objectClasses) / sizeof(container_objectClasses[0]);
|
||||
|
||||
for (int i = 0; i < container_objectClasses_size; i++) {
|
||||
if (entry_attribute_exists(e, "objectClass", container_objectClasses[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// "OU=Something,CN=Blah,CN=Bleh" => "Something"
|
||||
void first_element_in_dn(char* buffer, const char* dn, size_t buffer_size) {
|
||||
if (buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
*buffer = '\0';
|
||||
|
||||
if (dn == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove part before first "="
|
||||
const char* equals_ptr = strchr(dn, '=');
|
||||
if (equals_ptr == NULL || strlen(equals_ptr) <= 1) {
|
||||
return;
|
||||
}
|
||||
strncpy(buffer, equals_ptr + 1, buffer_size);
|
||||
|
||||
// Remove part after first ","
|
||||
char* comma_ptr = strchr(buffer, ',');
|
||||
if (comma_ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
*comma_ptr = '\0';
|
||||
}
|
||||
|
||||
// NOTE: doesn't handle multi-valued for now
|
||||
bool entry_edit_value(entry* e, char* key, char* new_value) {
|
||||
int result = ad_mod_replace(e->dn, key, new_value);
|
||||
|
||||
STR_ARRAY values = entry_get_attribute(e, key);
|
||||
|
||||
if (result == AD_SUCCESS) {
|
||||
free(values[0]);
|
||||
values[0] = strdup(new_value);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
printf("ad_mod_replace error: %s\n", ad_get_error());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void add_fake_attribute(entry* e, const char* key, const char* value) {
|
||||
STR_ARRAY values = NULL;
|
||||
arrput(values, strdup(value));
|
||||
shput(e->attributes, key, values);
|
||||
}
|
||||
|
||||
entry* make_fake_entry(const char* name, entry* parent, bool container, const char* category) {
|
||||
entry* e = (entry*)malloc(sizeof(entry));
|
||||
|
||||
char dn[1000];
|
||||
if (parent == NULL) {
|
||||
strcpy(dn, HEAD_DN);
|
||||
} else {
|
||||
strcpy(dn, "CN=");
|
||||
strcat(dn, name);
|
||||
strcat(dn, ",");
|
||||
strcat(dn, parent->dn);
|
||||
}
|
||||
|
||||
e->dn = strdup(dn);
|
||||
e->attributes = NULL;
|
||||
sh_new_strdup(e->attributes);
|
||||
e->attribute_keys = NULL;
|
||||
e->children = NULL;
|
||||
shput(entries, dn, e);
|
||||
|
||||
if (parent != NULL) {
|
||||
arrput(parent->children, strdup(e->dn));
|
||||
}
|
||||
|
||||
add_fake_attribute(e, "name", name);
|
||||
|
||||
if (container) {
|
||||
add_fake_attribute(e, "objectClass", "container");
|
||||
} else {
|
||||
add_fake_attribute(e, "objectClass", "class");
|
||||
}
|
||||
|
||||
add_fake_attribute(e, "objectCategory", category);
|
||||
|
||||
for (int i = 0; i < shlen(e->attributes); i++) {
|
||||
attributes_map a = e->attributes[i];
|
||||
arrput(e->attribute_keys, strdup(a.key));
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void entry_init_fake() {
|
||||
// Init map to use strdup for string allocation
|
||||
sh_new_strdup(entries);
|
||||
|
||||
// Load entries recursively
|
||||
entry_load(HEAD_DN);
|
||||
|
||||
// HEAD_DN= "DC=domain,DC=alt"
|
||||
entry* head = make_fake_entry(HEAD_DN, NULL, true, "Person");
|
||||
|
||||
entry* dave = make_fake_entry("dave", head, true, "Person");
|
||||
make_fake_entry("daves_dog", dave, false, "Robot");
|
||||
make_fake_entry("daves_car", dave, false, "Robot");
|
||||
add_fake_attribute(dave, "description", "dave is a cool dude");
|
||||
|
||||
entry* mark = make_fake_entry("mark", head, true, "Person");
|
||||
make_fake_entry("marks_son", mark, false, "Robot");
|
||||
make_fake_entry("marks_daughter", mark, false, "Robot");
|
||||
|
||||
entry* alice = make_fake_entry("alice", head, true, "Person");
|
||||
make_fake_entry("alices_child", alice, false, "Robot");
|
||||
make_fake_entry("alices_son", alice, false, "Person");
|
||||
make_fake_entry("alices_second_son", alice, false, "Person");
|
||||
|
||||
entry* advanced_bob = make_fake_entry("advanced_bob", head, true, "Person");
|
||||
make_fake_entry("advanced_bobs_son", advanced_bob, false, "Robot");
|
||||
make_fake_entry("advanced_bobs_daughter", advanced_bob, false, "Robot");
|
||||
add_fake_attribute(advanced_bob, "showInAdvancedViewOnly", "TRUE");
|
||||
|
||||
entry* advanced_klaren = make_fake_entry("advanced_klaren", head, true, "Person");
|
||||
make_fake_entry("advanced_klarens_son", advanced_klaren, false, "Robot");
|
||||
make_fake_entry("advanced_klarens_daughter", advanced_klaren, false, "Robot");
|
||||
add_fake_attribute(advanced_klaren, "showInAdvancedViewOnly", "TRUE");
|
||||
}
|
||||
|
||||
bool entry_new(const char* name, NewEntryType type) {
|
||||
return;
|
||||
|
||||
// TODO: compose parent DN
|
||||
// Users will be "HEAD_DN" + Users
|
||||
// Computers will be "HEAD_DN" + Computers
|
||||
char parent_dn[DN_LENGTH_MAX];
|
||||
switch (type) {
|
||||
case NewEntryType_User: {
|
||||
snrpintf("CN=Users,%s", DN_LENGTH_MAX, HEAD_DN);
|
||||
break;
|
||||
}
|
||||
case NewEntryType_Computer: {
|
||||
snrpintf("CN=Computers,%s", DN_LENGTH_MAX, HEAD_DN);
|
||||
break;
|
||||
}
|
||||
case NewEntryType_OU: {
|
||||
snrpintf("CN=Computers,%s", DN_LENGTH_MAX, HEAD_DN);
|
||||
break;
|
||||
}
|
||||
case NewEntryType_Group: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entry* parent = shget(entries, parent_dn);
|
||||
|
||||
char entry_dn[DN_LENGTH_MAX];
|
||||
snrpintf("CN=%s,%s", DN_LENGTH_MAX, name, parent->dn);
|
||||
|
||||
int result;
|
||||
switch (type) {
|
||||
case NewEntryType_User: {
|
||||
result = ad_create_user(name, entry_dn);
|
||||
break;
|
||||
}
|
||||
case NewEntryType_Computer: {
|
||||
result = ad_create_computer(name, entry_dn);
|
||||
break;
|
||||
}
|
||||
case NewEntryType_OU: {
|
||||
result = ad_ou_create(name, entry_dn);
|
||||
break;
|
||||
}
|
||||
case NewEntryType_Group: {
|
||||
result = ad_group_create(name, entry_dn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == AD_SUCCESS) {
|
||||
entry_load(entry_dn);
|
||||
|
||||
// Add new netry to parent's children
|
||||
STR_ARRAY parent_children = parent->children;
|
||||
arrput(parent_children, strdup(entry_dn));
|
||||
|
||||
// Replicate changes in view models
|
||||
containers_populate_model();
|
||||
contents_populate_model();
|
||||
} else {
|
||||
// TODO: display error in GUI
|
||||
printf("new entry error: %s\n", ad_get_error());
|
||||
}
|
||||
}
|
48
src/entry.h
48
src/entry.h
@ -1,48 +0,0 @@
|
||||
|
||||
#ifndef ENTRY_H
|
||||
#define ENTRY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// for use with stb array functions
|
||||
typedef char** STR_ARRAY;
|
||||
|
||||
typedef struct attributes_map {
|
||||
char* key;
|
||||
STR_ARRAY value;
|
||||
} attributes_map;
|
||||
|
||||
typedef struct entry {
|
||||
char* dn;
|
||||
attributes_map* attributes;
|
||||
STR_ARRAY attribute_keys;
|
||||
STR_ARRAY children;
|
||||
} entry;
|
||||
|
||||
typedef struct entries_map {
|
||||
char* key;
|
||||
entry* value;
|
||||
} entries_map;
|
||||
|
||||
typedef enum {
|
||||
NewEntryType_User,
|
||||
NewEntryType_Computer,
|
||||
NewEntryType_OU,
|
||||
NewEntryType_Group
|
||||
} NewEntryType;
|
||||
|
||||
void entry_init();
|
||||
void entry_init_fake();
|
||||
void entry_load(const char* dn);
|
||||
STR_ARRAY entry_get_attribute(entry* e, const char* key);
|
||||
char* entry_get_attribute_or_none(entry* e, const char* key);
|
||||
bool entry_new(const char* name, NewEntryType type);
|
||||
entry* get_entry(const char* dn);
|
||||
void entry_delete(entry* e);
|
||||
bool entry_attribute_exists(entry* e, const char* key, const char* value);
|
||||
bool entry_is_container(entry* e);
|
||||
void first_element_in_dn(char* buffer, const char* dn, size_t buffer_size);
|
||||
bool entry_edit_value(entry* e, char* key, char* new_value);
|
||||
|
||||
#endif
|
1828
src/libs/stb_ds.h
1828
src/libs/stb_ds.h
File diff suppressed because it is too large
Load Diff
65
src/main.c
65
src/main.c
@ -1,65 +0,0 @@
|
||||
|
||||
// #include "mainwindow.h"
|
||||
#include "containers_view.h"
|
||||
#include "contents_view.h"
|
||||
#include "attributes_view.h"
|
||||
#include "menu_bar.h"
|
||||
#include "entry.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "active_directory.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// TODO: show some kind of progress indicator before loading data? it's not instant
|
||||
|
||||
bool fake_entries = false;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp(argv[i], "fake") == 0) {
|
||||
fake_entries = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fake_entries) {
|
||||
entry_init_fake();
|
||||
} else {
|
||||
// Login into ldap
|
||||
LDAP* ldap_connection = ad_login();
|
||||
if (ldap_connection == NULL) {
|
||||
printf("ad_login error: %s\n", ad_get_error());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load entry data
|
||||
entry_init();
|
||||
}
|
||||
|
||||
|
||||
// Setup UI
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
// Load builder
|
||||
GtkBuilder *builder = gtk_builder_new_from_file("data/adtool.glade");
|
||||
if (builder == NULL) {
|
||||
printf("Failed to load glade file\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: inits have to be in this order
|
||||
attributes_init(builder);
|
||||
contents_init(builder);
|
||||
containers_init(builder);
|
||||
menu_bar_init(builder);
|
||||
|
||||
gtk_builder_connect_signals(builder, NULL);
|
||||
|
||||
GtkWidget* window = GTK_WIDGET(gtk_builder_get_object_CHECKED(builder, "window"));
|
||||
gtk_widget_show(window);
|
||||
|
||||
g_object_unref(G_OBJECT(builder));
|
||||
|
||||
gtk_main();
|
||||
|
||||
return 0;
|
||||
}
|
81
src/main.cpp
Normal file
81
src/main.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
|
||||
#include "containers_view.h"
|
||||
#include "contents_view.h"
|
||||
#include "attributes_view.h"
|
||||
#include "ad_filter.h"
|
||||
#include "ad_model.h"
|
||||
#include "attributes_model.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "ad_interface.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QString>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Load fake AD data if given "fake" argument
|
||||
// This also swaps all ad interface functions to their fake versions (including login)
|
||||
if (argc >= 1 && QString(argv[0]) == "fake") {
|
||||
FAKE_AD = true;
|
||||
}
|
||||
|
||||
if (!ad_interface_login()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
//
|
||||
// Setup ui
|
||||
//
|
||||
Ui::MainWindow ui;
|
||||
QMainWindow main_window;
|
||||
ui.setupUi(&main_window);
|
||||
|
||||
AdModel ad_model;
|
||||
|
||||
// Attributes
|
||||
{
|
||||
auto attributes_model = new AttributesModel();
|
||||
ui.attributes_view->setModel(attributes_model);
|
||||
}
|
||||
|
||||
// Contents
|
||||
{
|
||||
auto view = ui.contents_view;
|
||||
|
||||
auto proxy = new AdFilter(ui.menubar_view_advancedView);
|
||||
proxy->setSourceModel(&ad_model);
|
||||
view->setModel(proxy);
|
||||
}
|
||||
|
||||
// Containers
|
||||
{
|
||||
auto view = ui.containers_view;
|
||||
|
||||
auto proxy = new AdFilter(ui.menubar_view_advancedView, true);
|
||||
proxy->setSourceModel(&ad_model);
|
||||
view->setModel(proxy);
|
||||
|
||||
// NOTE: have to hide columns after setting model
|
||||
view->hideColumn(AdModel::Column::Category);
|
||||
view->hideColumn(AdModel::Column::Description);
|
||||
}
|
||||
|
||||
//
|
||||
// Connect signals
|
||||
//
|
||||
|
||||
// Set root index of contents view to selection of containers view
|
||||
QObject::connect(
|
||||
ui.containers_view->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
ui.contents_view, &ContentsView::set_root_index_from_selection);
|
||||
|
||||
// Set target of attributes view to selection of contents view
|
||||
QObject::connect(
|
||||
ui.contents_view->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
ui.attributes_view, &AttributesView::set_target_from_selection);
|
||||
|
||||
main_window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
|
||||
#include "menu_bar.h"
|
||||
#include "containers_view.h"
|
||||
#include "contents_view.h"
|
||||
#include "entry.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool advanced_view = false;
|
||||
|
||||
GtkDialog* new_user_dialog = NULL;
|
||||
GtkEntry* new_user_name_entry = NULL;
|
||||
|
||||
bool advanced_view_is_on() {
|
||||
return advanced_view;
|
||||
}
|
||||
|
||||
void advanced_view_toggled_func(GtkWidget *widget, gpointer data) {
|
||||
advanced_view = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
|
||||
|
||||
containers_refilter();
|
||||
contents_refilter();
|
||||
}
|
||||
|
||||
void menu_bar_file_new_user_func(GtkWidget *widget, gpointer data) {
|
||||
// TODO: set max chars for entry (in glade) based on ldap limits
|
||||
int response = gtk_dialog_run(new_user_dialog);
|
||||
|
||||
if (response == GTK_RESPONSE_OK) {
|
||||
printf("new user is %s\n", gtk_entry_get_text(new_user_name_entry));
|
||||
|
||||
entry_new(gtk_entry_get_text(new_user_name_entry), NewEntryType_User);
|
||||
|
||||
gtk_entry_set_text(new_user_name_entry, "");
|
||||
gtk_widget_hide(GTK_WIDGET(new_user_dialog));
|
||||
} else if (response == GTK_RESPONSE_CANCEL) {
|
||||
gtk_entry_set_text(new_user_name_entry, "");
|
||||
gtk_widget_hide(GTK_WIDGET(new_user_dialog));
|
||||
} else {
|
||||
printf("ERROR: menu_bar_file_new_user_func() received unknown button response %d !\n", response);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_bar_init(GtkBuilder* builder) {
|
||||
new_user_dialog = GTK_DIALOG(gtk_builder_get_object_CHECKED(builder, "new_user_dialog"));
|
||||
new_user_name_entry = GTK_ENTRY(gtk_builder_get_object_CHECKED(builder, "new_user_dialog_name_entry"));
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
bool advanced_view_is_on();
|
||||
void menu_bar_init(GtkBuilder* builder);
|
142
src/ui/ui_mainwindow.h
Normal file
142
src/ui/ui_mainwindow.h
Normal file
@ -0,0 +1,142 @@
|
||||
/********************************************************************************
|
||||
** Form generated from reading UI file 'mainwindow.ui'
|
||||
**
|
||||
** Created by: Qt User Interface Compiler version 5.12.8
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef UI_MAINWINDOW_H
|
||||
#define UI_MAINWINDOW_H
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtWidgets/QAction>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QSplitter>
|
||||
#include <QtWidgets/QStatusBar>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include "src/attributes_view.h"
|
||||
#include "src/containers_view.h"
|
||||
#include "src/contents_view.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Ui_MainWindow
|
||||
{
|
||||
public:
|
||||
QAction *actionWhat;
|
||||
QAction *actionSecond;
|
||||
QAction *actionSomething;
|
||||
QAction *actionHere;
|
||||
QAction *menubar_view_advancedView;
|
||||
QWidget *centralwidget;
|
||||
QSplitter *splitter;
|
||||
ContainersView *containers_view;
|
||||
ContentsView *contents_view;
|
||||
AttributesView *attributes_view;
|
||||
QMenuBar *menubar;
|
||||
QMenu *menuhello;
|
||||
QMenu *menuEdit;
|
||||
QMenu *menuView;
|
||||
QStatusBar *statusbar;
|
||||
|
||||
void setupUi(QMainWindow *MainWindow)
|
||||
{
|
||||
if (MainWindow->objectName().isEmpty())
|
||||
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
|
||||
MainWindow->resize(1307, 637);
|
||||
actionWhat = new QAction(MainWindow);
|
||||
actionWhat->setObjectName(QString::fromUtf8("actionWhat"));
|
||||
actionSecond = new QAction(MainWindow);
|
||||
actionSecond->setObjectName(QString::fromUtf8("actionSecond"));
|
||||
actionSomething = new QAction(MainWindow);
|
||||
actionSomething->setObjectName(QString::fromUtf8("actionSomething"));
|
||||
actionHere = new QAction(MainWindow);
|
||||
actionHere->setObjectName(QString::fromUtf8("actionHere"));
|
||||
menubar_view_advancedView = new QAction(MainWindow);
|
||||
menubar_view_advancedView->setObjectName(QString::fromUtf8("menubar_view_advancedView"));
|
||||
menubar_view_advancedView->setCheckable(true);
|
||||
centralwidget = new QWidget(MainWindow);
|
||||
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
|
||||
splitter = new QSplitter(centralwidget);
|
||||
splitter->setObjectName(QString::fromUtf8("splitter"));
|
||||
splitter->setGeometry(QRect(0, 0, 1301, 591));
|
||||
splitter->setOrientation(Qt::Horizontal);
|
||||
containers_view = new ContainersView(splitter);
|
||||
containers_view->setObjectName(QString::fromUtf8("containers_view"));
|
||||
containers_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
containers_view->setRootIsDecorated(true);
|
||||
containers_view->setItemsExpandable(true);
|
||||
containers_view->setExpandsOnDoubleClick(true);
|
||||
splitter->addWidget(containers_view);
|
||||
containers_view->header()->setVisible(true);
|
||||
contents_view = new ContentsView(splitter);
|
||||
contents_view->setObjectName(QString::fromUtf8("contents_view"));
|
||||
contents_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
contents_view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
contents_view->setRootIsDecorated(false);
|
||||
contents_view->setItemsExpandable(false);
|
||||
contents_view->setExpandsOnDoubleClick(false);
|
||||
splitter->addWidget(contents_view);
|
||||
contents_view->header()->setVisible(true);
|
||||
attributes_view = new AttributesView(splitter);
|
||||
attributes_view->setObjectName(QString::fromUtf8("attributes_view"));
|
||||
attributes_view->setEditTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed);
|
||||
attributes_view->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
attributes_view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
splitter->addWidget(attributes_view);
|
||||
MainWindow->setCentralWidget(centralwidget);
|
||||
menubar = new QMenuBar(MainWindow);
|
||||
menubar->setObjectName(QString::fromUtf8("menubar"));
|
||||
menubar->setGeometry(QRect(0, 0, 1307, 27));
|
||||
menuhello = new QMenu(menubar);
|
||||
menuhello->setObjectName(QString::fromUtf8("menuhello"));
|
||||
menuEdit = new QMenu(menubar);
|
||||
menuEdit->setObjectName(QString::fromUtf8("menuEdit"));
|
||||
menuView = new QMenu(menubar);
|
||||
menuView->setObjectName(QString::fromUtf8("menuView"));
|
||||
MainWindow->setMenuBar(menubar);
|
||||
statusbar = new QStatusBar(MainWindow);
|
||||
statusbar->setObjectName(QString::fromUtf8("statusbar"));
|
||||
MainWindow->setStatusBar(statusbar);
|
||||
|
||||
menubar->addAction(menuhello->menuAction());
|
||||
menubar->addAction(menuEdit->menuAction());
|
||||
menubar->addAction(menuView->menuAction());
|
||||
menuhello->addAction(actionWhat);
|
||||
menuhello->addAction(actionSecond);
|
||||
menuEdit->addAction(actionSomething);
|
||||
menuEdit->addAction(actionHere);
|
||||
menuView->addAction(menubar_view_advancedView);
|
||||
|
||||
retranslateUi(MainWindow);
|
||||
|
||||
QMetaObject::connectSlotsByName(MainWindow);
|
||||
} // setupUi
|
||||
|
||||
void retranslateUi(QMainWindow *MainWindow)
|
||||
{
|
||||
MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", nullptr));
|
||||
actionWhat->setText(QApplication::translate("MainWindow", "What", nullptr));
|
||||
actionSecond->setText(QApplication::translate("MainWindow", "Second", nullptr));
|
||||
actionSomething->setText(QApplication::translate("MainWindow", "Something", nullptr));
|
||||
actionHere->setText(QApplication::translate("MainWindow", "Here", nullptr));
|
||||
menubar_view_advancedView->setText(QApplication::translate("MainWindow", "Advanced view", nullptr));
|
||||
menuhello->setTitle(QApplication::translate("MainWindow", "New", nullptr));
|
||||
menuEdit->setTitle(QApplication::translate("MainWindow", "Edit", nullptr));
|
||||
menuView->setTitle(QApplication::translate("MainWindow", "View", nullptr));
|
||||
} // retranslateUi
|
||||
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow: public Ui_MainWindow {};
|
||||
} // namespace Ui
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // UI_MAINWINDOW_H
|
22
src/utils.c
22
src/utils.c
@ -1,22 +0,0 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool streql(const char* a, const char* b) {
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
// Use this instead of regular gtk_builder_get_object() so that
|
||||
// on error the object's name is printed
|
||||
GObject* gtk_builder_get_object_CHECKED(GtkBuilder* builder, const char *name) {
|
||||
GObject* object = gtk_builder_get_object(builder, name);
|
||||
|
||||
if (object != NULL) {
|
||||
return object;
|
||||
} else {
|
||||
printf("ERROR: gtk_builder_get_object() failed to get \"%s\"\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
bool streql(const char* a, const char* b);
|
||||
GObject* gtk_builder_get_object_CHECKED(GtkBuilder* builder, const char *name);
|
Loading…
x
Reference in New Issue
Block a user