Merge branch 'salt' into controllers
|
|
@ -1,117 +1,213 @@
|
|||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LogViewerDialog</class>
|
||||
<widget class="QDialog" name="LogViewerDialog" >
|
||||
<property name="geometry" >
|
||||
<widget class="QDialog" name="LogViewerDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>578</width>
|
||||
<height>579</height>
|
||||
<width>516</width>
|
||||
<height>287</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>Log Viewer</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<layout class="QGridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="3" >
|
||||
<widget class="QPushButton" name="clear_pb" >
|
||||
<property name="text" >
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4" >
|
||||
<widget class="QPushButton" name="separator_pb" >
|
||||
<property name="text" >
|
||||
<string>Separator</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5" >
|
||||
<widget class="QPushButton" name="copy_pb" >
|
||||
<property name="text" >
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" >
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>101</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>Verbosity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QComboBox" name="verbosity_cbx" >
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="verbosity_cbx">
|
||||
<item>
|
||||
<property name="text" >
|
||||
<property name="text">
|
||||
<string>Silent</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<property name="text">
|
||||
<string>Information</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<property name="text">
|
||||
<string>Details</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<property name="text">
|
||||
<string>Verbose</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<property name="text">
|
||||
<string>Noisy</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="6" >
|
||||
<widget class="QListView" name="log_view" >
|
||||
<property name="resizeMode" >
|
||||
<enum>QListView::Adjust</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes" >
|
||||
<bool>true</bool>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="separator_pb">
|
||||
<property name="text">
|
||||
<string>Separator</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="6" >
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<item row="0" column="5">
|
||||
<widget class="QPushButton" name="copy_pb">
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QPushButton" name="clear_pb">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="verbosity_label">
|
||||
<property name="text">
|
||||
<string>Verbosity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="6">
|
||||
<widget class="QListView" name="log_view">
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Adjust</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>101</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="6">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="attn_frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="layResources.qrc">:/warn_16.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>There are errors or warnings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<tabstops>
|
||||
<tabstop>verbosity_cbx</tabstop>
|
||||
<tabstop>clear_pb</tabstop>
|
||||
<tabstop>separator_pb</tabstop>
|
||||
<tabstop>copy_pb</tabstop>
|
||||
<tabstop>log_view</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="layResources.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
|
|
@ -119,11 +215,11 @@
|
|||
<receiver>LogViewerDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
|
|
@ -135,11 +231,11 @@
|
|||
<receiver>LogViewerDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,837 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SaltGrainPropertiesDialog</class>
|
||||
<widget class="QDialog" name="SaltGrainPropertiesDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>693</width>
|
||||
<height>538</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Package Properties</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="7" column="2" colspan="3">
|
||||
<widget class="QTextEdit" name="doc">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="2" colspan="3">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="2" colspan="3">
|
||||
<widget class="QLineEdit" name="author"/>
|
||||
</item>
|
||||
<item row="8" column="4">
|
||||
<widget class="QLabel" name="open_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><a href="%1">Open link</a></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Author contact</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>License</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="2" colspan="3">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="2" colspan="3">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Depends on</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" colspan="3">
|
||||
<widget class="QLineEdit" name="title"/>
|
||||
</item>
|
||||
<item row="9" column="2" colspan="3">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(enter an URL to provide a documentation link)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2" colspan="3">
|
||||
<widget class="QLineEdit" name="author_contact"/>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<widget class="QFrame" name="frame_5">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="license">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::AlertLogButton" name="license_alert">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/warn.png</normaloff>:/warn.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QFrame" name="frame_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="version">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::AlertLogButton" name="version_alert">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/warn.png</normaloff>:/warn.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="2" colspan="3">
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="2">
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="remove_dependency">
|
||||
<property name="toolTip">
|
||||
<string>Delete dependency</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QToolButton" name="add_dependency">
|
||||
<property name="toolTip">
|
||||
<string>Add new dependency</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/add.png</normaloff>:/add.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="3">
|
||||
<widget class="QTreeWidget" name="dependencies">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>URL</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="lay::AlertLogButton" name="dependencies_alert">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/warn.png</normaloff>:/warn.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Images</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3" colspan="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(use numeric versions like "1.5" or "2.1.3")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Author</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="2" colspan="3">
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="6">
|
||||
<widget class="QToolButton" name="screenshot_delete_button">
|
||||
<property name="toolTip">
|
||||
<string>Reset Screenshot</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/clear_edit.png</normaloff>:/clear_edit.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Icon
|
||||
(64x64)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" rowspan="4">
|
||||
<widget class="QToolButton" name="icon_config_button">
|
||||
<property name="toolTip">
|
||||
<string>Select an Icon Image</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/salt_icon.png</normaloff>:/salt_icon.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5" rowspan="4">
|
||||
<widget class="QToolButton" name="screenshot_config_button">
|
||||
<property name="toolTip">
|
||||
<string>Select a Screenshot Image</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/add.png</normaloff>:/add.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="7">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="3" rowspan="4">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="2" rowspan="3">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="icon_delete_button">
|
||||
<property name="toolTip">
|
||||
<string>Reset Icon</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/clear_edit.png</normaloff>:/clear_edit.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Showcase image
|
||||
(max 1024x1024)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Documentation</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="3" colspan="2">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(license information like "GPLv3" or "MIT")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="2" colspan="2">
|
||||
<widget class="QFrame" name="frame_6">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="doc_url">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::AlertLogButton" name="doc_url_alert">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/warn.png</normaloff>:/warn.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>lay::AlertLogButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>layLogViewerDialog.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>version</tabstop>
|
||||
<tabstop>title</tabstop>
|
||||
<tabstop>author</tabstop>
|
||||
<tabstop>author_contact</tabstop>
|
||||
<tabstop>license</tabstop>
|
||||
<tabstop>doc</tabstop>
|
||||
<tabstop>doc_url</tabstop>
|
||||
<tabstop>icon_config_button</tabstop>
|
||||
<tabstop>icon_delete_button</tabstop>
|
||||
<tabstop>screenshot_config_button</tabstop>
|
||||
<tabstop>screenshot_delete_button</tabstop>
|
||||
<tabstop>dependencies</tabstop>
|
||||
<tabstop>add_dependency</tabstop>
|
||||
<tabstop>remove_dependency</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="layResources.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SaltGrainPropertiesDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>546</x>
|
||||
<y>425</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>561</x>
|
||||
<y>435</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SaltGrainPropertiesDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>638</x>
|
||||
<y>418</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>649</x>
|
||||
<y>433</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SaltGrainTemplateSelectionDialog</class>
|
||||
<widget class="QDialog" name="SaltGrainTemplateSelectionDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>401</width>
|
||||
<height>499</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create Package</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Template</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="salt_view">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="selectionRectVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(Pick a template with which to initialize your new package)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Package Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="name_edit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::AlertLogButton" name="name_alert">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/warn.png</normaloff>:/warn.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<italic>true</italic>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(Choose a simple name composed of letters, digits and underscores. Use the notation "group/package" to create a package inside a group)</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionNew">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/add.png</normaloff>:/add.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>New package</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete package</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/import.png</normaloff>:/import.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import package</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>lay::AlertLogButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>layLogViewerDialog.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="layResources.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SaltGrainTemplateSelectionDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>273</x>
|
||||
<y>431</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>276</x>
|
||||
<y>448</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SaltGrainTemplateSelectionDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>351</x>
|
||||
<y>426</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>363</x>
|
||||
<y>445</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,798 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SaltManagerDialog</class>
|
||||
<widget class="QDialog" name="SaltManagerDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>692</width>
|
||||
<height>440</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Salt Package Manager</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="mode_tab">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Browse Installed Packages</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="create_button">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create New Package</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/add.png</normaloff>:/add.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="delete_button">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uninstall Package</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="layResources.qrc">:/find.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::DecoratedLineEdit" name="search_installed_edit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="list_stack">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="salt_view">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="selectionRectVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>343</width>
|
||||
<height>207</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="3" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="layResources.qrc">:/salt.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><h1>Salt Package Manager</h1></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><body><h4>No packages are installed currently.</h4><p>Use<br/>
|
||||
<table>
|
||||
<tr><td>The "Install New Packages" tab to install a package<br/>from an external repository</td></tr>
|
||||
<tr></tr>
|
||||
<tr><td>The <a href=":add"><img src=":/add.png"></a> button to create a new package</td></tr>
|
||||
</table>
|
||||
</body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QFrame" name="details_frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Details</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QToolButton" name="edit_button">
|
||||
<property name="toolTip">
|
||||
<string>Edit Package Details</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/edit.png</normaloff>:/edit.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="lay::SaltGrainDetailsTextWidget" name="details_text">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Install or Update Packages</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter_new">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_5">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="mark_button">
|
||||
<property name="toolTip">
|
||||
<string>Mark or unmark for installation</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mark</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/marked_16.png</normaloff>:/marked_16.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>126</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="layResources.qrc">:/find.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::DecoratedLineEdit" name="search_new_edit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="salt_mine_view">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="selectionRectVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QFrame" name="details_new_frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_4">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Details</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="toolButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/empty_16.png</normaloff>:/empty_16.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::SaltGrainDetailsTextWidget" name="details_new_text">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_6">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="apply_button">
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="apply_label">
|
||||
<property name="text">
|
||||
<string>Set in code</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>563</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionNew">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/add.png</normaloff>:/add.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>New package</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete package</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUnmarkAll">
|
||||
<property name="text">
|
||||
<string>Unmark all</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShowMarkedOnly">
|
||||
<property name="text">
|
||||
<string>Show marked only</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShowAll">
|
||||
<property name="text">
|
||||
<string>Show all</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>lay::DecoratedLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>layWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>lay::SaltGrainDetailsTextWidget</class>
|
||||
<extends>QTextBrowser</extends>
|
||||
<header>laySaltGrainDetailsTextWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>mode_tab</tabstop>
|
||||
<tabstop>search_installed_edit</tabstop>
|
||||
<tabstop>salt_view</tabstop>
|
||||
<tabstop>details_text</tabstop>
|
||||
<tabstop>edit_button</tabstop>
|
||||
<tabstop>search_new_edit</tabstop>
|
||||
<tabstop>mark_button</tabstop>
|
||||
<tabstop>salt_mine_view</tabstop>
|
||||
<tabstop>details_new_text</tabstop>
|
||||
<tabstop>toolButton</tabstop>
|
||||
<tabstop>apply_button</tabstop>
|
||||
<tabstop>scrollArea</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="layResources.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>clicked(QAbstractButton*)</signal>
|
||||
<receiver>SaltManagerDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>635</x>
|
||||
<y>421</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>653</x>
|
||||
<y>432</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SaltManagerInstallConfirmationDialog</class>
|
||||
<widget class="QDialog" name="SaltManagerInstallConfirmationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>495</width>
|
||||
<height>478</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Ready for Installation</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>The following packages are now ready for installation or update:</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="list">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Package</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Action</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Download link</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Press "Ok" to install or update these packages or "Cancel" to abort.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>6</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionNew">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/add.png</normaloff>:/add.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>New package</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete package</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport">
|
||||
<property name="icon">
|
||||
<iconset resource="layResources.qrc">
|
||||
<normaloff>:/import.png</normaloff>:/import.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import package</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="layResources.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SaltManagerInstallConfirmationDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>273</x>
|
||||
<y>431</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>276</x>
|
||||
<y>448</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SaltManagerInstallConfirmationDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>351</x>
|
||||
<y>426</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>363</x>
|
||||
<y>445</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -539,6 +539,10 @@ Class<lay::MainWindow> decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "MainWind
|
|||
"@brief 'cm_technologies' action (bound to a menu)"
|
||||
"\nThis method has been added in version 0.22."
|
||||
) +
|
||||
gsi::method ("cm_packages", &lay::MainWindow::cm_packages,
|
||||
"@brief 'cm_packages' action (bound to a menu)"
|
||||
"\nThis method has been added in version 0.25."
|
||||
) +
|
||||
gsi::method ("cm_open_too", &lay::MainWindow::cm_open_too,
|
||||
"@brief 'cm_open_too' action (bound to a menu)"
|
||||
) +
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 149 B |
|
After Width: | Height: | Size: 674 B |
|
After Width: | Height: | Size: 499 B |
|
After Width: | Height: | Size: 336 B |
|
After Width: | Height: | Size: 424 B |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 637 B |
|
After Width: | Height: | Size: 637 B |
|
|
@ -47,7 +47,15 @@ HEADERS = \
|
|||
layCommon.h \
|
||||
layConfig.h \
|
||||
layMacroController.h \
|
||||
layTechnologyController.h
|
||||
layTechnologyController.h \
|
||||
laySalt.h \
|
||||
laySaltGrain.h \
|
||||
laySaltGrains.h \
|
||||
laySaltManagerDialog.h \
|
||||
laySaltGrainDetailsTextWidget.h \
|
||||
laySaltGrainPropertiesDialog.h \
|
||||
laySaltDownloadManager.h \
|
||||
laySaltModel.h
|
||||
|
||||
FORMS = \
|
||||
ClipDialog.ui \
|
||||
|
|
@ -92,7 +100,11 @@ FORMS = \
|
|||
XORToolDialog.ui \
|
||||
TechLoadOptionsEditorPage.ui \
|
||||
TechSaveOptionsEditorPage.ui \
|
||||
MainConfigPage7.ui
|
||||
MainConfigPage7.ui \
|
||||
SaltManagerDialog.ui \
|
||||
SaltGrainPropertiesDialog.ui \
|
||||
SaltGrainTemplateSelectionDialog.ui \
|
||||
SaltManagerInstallConfirmationDialog.ui
|
||||
|
||||
SOURCES = \
|
||||
gsiDeclLayApplication.cc \
|
||||
|
|
@ -136,13 +148,22 @@ SOURCES = \
|
|||
layTextProgress.cc \
|
||||
layVersion.cc \
|
||||
layMacroController.cc \
|
||||
layTechnologyController.cc
|
||||
layTechnologyController.cc \
|
||||
laySalt.cc \
|
||||
laySaltGrain.cc \
|
||||
laySaltGrains.cc \
|
||||
laySaltManagerDialog.cc \
|
||||
laySaltGrainDetailsTextWidget.cc \
|
||||
laySaltGrainPropertiesDialog.cc \
|
||||
laySaltDownloadManager.cc \
|
||||
laySaltModel.cc
|
||||
|
||||
RESOURCES = layBuildInMacros.qrc \
|
||||
layHelpResources.qrc \
|
||||
layLayoutStatistics.qrc \
|
||||
layMacroTemplates.qrc \
|
||||
layResources.qrc \
|
||||
laySaltTemplates.qrc
|
||||
|
||||
INCLUDEPATH += ../tl ../gsi ../db ../rdb ../laybasic ../ant ../img ../edt
|
||||
DEPENDPATH += ../tl ../gsi ../db ../rdb ../laybasic ../ant ../img ../edt
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <QMutexLocker>
|
||||
#include <QTimer>
|
||||
#include <QClipboard>
|
||||
#include <QFrame>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
|
@ -97,25 +98,30 @@ LogReceiver::begin ()
|
|||
// -----------------------------------------------------------------
|
||||
// LogFile implementation
|
||||
|
||||
LogFile::LogFile (size_t max_entries)
|
||||
: m_error_receiver (this, 0, &LogFile::error),
|
||||
m_warn_receiver (this, 0, &LogFile::warn),
|
||||
m_log_receiver (this, 10, &LogFile::info),
|
||||
m_info_receiver (this, 0, &LogFile::info),
|
||||
LogFile::LogFile (size_t max_entries, bool register_global)
|
||||
: m_error_receiver (this, 0, &LogFile::add_error),
|
||||
m_warn_receiver (this, 0, &LogFile::add_warn),
|
||||
m_log_receiver (this, 10, &LogFile::add_info),
|
||||
m_info_receiver (this, 0, &LogFile::add_info),
|
||||
m_max_entries (max_entries),
|
||||
m_generation_id (0),
|
||||
m_last_generation_id (0)
|
||||
m_last_generation_id (0),
|
||||
m_has_errors (false),
|
||||
m_has_warnings (false),
|
||||
m_last_attn (false)
|
||||
{
|
||||
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
|
||||
|
||||
m_timer.setSingleShot (true);
|
||||
m_timer.setSingleShot (false);
|
||||
m_timer.setInterval (100);
|
||||
m_timer.start ();
|
||||
|
||||
tl::info.add (&m_info_receiver, false);
|
||||
tl::log.add (&m_log_receiver, false);
|
||||
tl::error.add (&m_error_receiver, false);
|
||||
tl::warn.add (&m_warn_receiver, false);
|
||||
if (register_global) {
|
||||
tl::info.add (&m_info_receiver, false);
|
||||
tl::log.add (&m_log_receiver, false);
|
||||
tl::error.add (&m_error_receiver, false);
|
||||
tl::warn.add (&m_warn_receiver, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -123,8 +129,23 @@ LogFile::clear ()
|
|||
{
|
||||
QMutexLocker locker (&m_lock);
|
||||
|
||||
m_messages.clear ();
|
||||
++m_generation_id;
|
||||
if (!m_messages.empty ()) {
|
||||
m_messages.clear ();
|
||||
m_has_errors = m_has_warnings = false;
|
||||
++m_generation_id;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LogFile::has_errors () const
|
||||
{
|
||||
return m_has_errors;
|
||||
}
|
||||
|
||||
bool
|
||||
LogFile::has_warnings () const
|
||||
{
|
||||
return m_has_warnings;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -159,9 +180,13 @@ void
|
|||
LogFile::timeout ()
|
||||
{
|
||||
bool changed = false;
|
||||
bool attn = false, last_attn = false;
|
||||
|
||||
m_lock.lock ();
|
||||
if (m_generation_id != m_last_generation_id) {
|
||||
attn = m_has_errors || m_has_warnings;
|
||||
last_attn = m_last_attn;
|
||||
m_last_attn = attn;
|
||||
m_last_generation_id = m_generation_id;
|
||||
changed = true;
|
||||
}
|
||||
|
|
@ -169,9 +194,10 @@ LogFile::timeout ()
|
|||
|
||||
if (changed) {
|
||||
emit layoutChanged ();
|
||||
if (last_attn != attn) {
|
||||
emit attention_changed (attn);
|
||||
}
|
||||
}
|
||||
|
||||
m_timer.start ();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -183,6 +209,12 @@ LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continu
|
|||
m_messages.pop_front ();
|
||||
}
|
||||
|
||||
if (mode == LogFileEntry::Warning || mode == LogFileEntry::WarningContinued) {
|
||||
m_has_warnings = true;
|
||||
} else if (mode == LogFileEntry::Error || mode == LogFileEntry::ErrorContinued) {
|
||||
m_has_errors = true;
|
||||
}
|
||||
|
||||
m_messages.push_back (LogFileEntry (mode, msg, continued));
|
||||
|
||||
++m_generation_id;
|
||||
|
|
@ -201,18 +233,25 @@ LogFile::data(const QModelIndex &index, int role) const
|
|||
{
|
||||
QMutexLocker locker (&m_lock);
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (role == Qt::DecorationRole) {
|
||||
|
||||
if (index.row () < int (m_messages.size ()) && index.row () >= 0) {
|
||||
LogFileEntry::mode_type mode = m_messages [index.row ()].mode ();
|
||||
std::string message = m_messages [index.row ()].text ();
|
||||
if (mode == LogFileEntry::Error) {
|
||||
return QVariant (tl::to_qstring (tl::to_string (QObject::tr ("ERROR: ")) + message));
|
||||
return QIcon (QString::fromUtf8 (":/error_16.png"));
|
||||
} else if (mode == LogFileEntry::Warning) {
|
||||
return QVariant (tl::to_qstring (tl::to_string (QObject::tr ("Warning: ")) + message));
|
||||
return QIcon (QString::fromUtf8 (":/warn_16.png"));
|
||||
} else if (mode == LogFileEntry::Info) {
|
||||
return QIcon (QString::fromUtf8 (":/info_16.png"));
|
||||
} else {
|
||||
return QVariant (tl::to_qstring (message));
|
||||
return QIcon (QString::fromUtf8 (":/empty_16.png"));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (role == Qt::DisplayRole) {
|
||||
|
||||
if (index.row () < int (m_messages.size ()) && index.row () >= 0) {
|
||||
return QVariant (tl::to_qstring (m_messages [index.row ()].text ()));
|
||||
}
|
||||
|
||||
} else if (role == Qt::FontRole) {
|
||||
|
|
@ -251,21 +290,36 @@ LogFile::data(const QModelIndex &index, int role) const
|
|||
// -----------------------------------------------------------------
|
||||
// LogViewerDialog implementation
|
||||
|
||||
LogViewerDialog::LogViewerDialog (QWidget *parent)
|
||||
LogViewerDialog::LogViewerDialog (QWidget *parent, bool register_global, bool interactive)
|
||||
: QDialog (parent),
|
||||
m_file (50000) // TODO: make this variable ..
|
||||
m_file (50000, register_global) // TODO: make this variable ..
|
||||
{
|
||||
setupUi (this);
|
||||
|
||||
// For non-global log views, the verbosity selector does not make sense
|
||||
if (!register_global) {
|
||||
verbosity_cbx->hide ();
|
||||
verbosity_label->hide ();
|
||||
} else {
|
||||
verbosity_cbx->setCurrentIndex (std::min (4, tl::verbosity () / 10));
|
||||
connect (verbosity_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (verbosity_changed (int)));
|
||||
}
|
||||
|
||||
if (!interactive) {
|
||||
clear_pb->hide ();
|
||||
separator_pb->hide ();
|
||||
copy_pb->hide ();
|
||||
} else {
|
||||
connect (clear_pb, SIGNAL (clicked ()), &m_file, SLOT (clear ()));
|
||||
connect (separator_pb, SIGNAL (clicked ()), &m_file, SLOT (separator ()));
|
||||
connect (copy_pb, SIGNAL (clicked ()), &m_file, SLOT (copy ()));
|
||||
}
|
||||
|
||||
attn_frame->hide ();
|
||||
log_view->setModel (&m_file);
|
||||
|
||||
verbosity_cbx->setCurrentIndex (std::min (4, tl::verbosity () / 10));
|
||||
|
||||
connect (&m_file, SIGNAL (layoutChanged ()), log_view, SLOT (scrollToBottom ()));
|
||||
connect (verbosity_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (verbosity_changed (int)));
|
||||
connect (clear_pb, SIGNAL (clicked ()), &m_file, SLOT (clear ()));
|
||||
connect (separator_pb, SIGNAL (clicked ()), &m_file, SLOT (separator ()));
|
||||
connect (copy_pb, SIGNAL (clicked ()), &m_file, SLOT (copy ()));
|
||||
connect (&m_file, SIGNAL (attention_changed (bool)), attn_frame, SLOT (setVisible (bool)));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -274,5 +328,56 @@ LogViewerDialog::verbosity_changed (int index)
|
|||
tl::verbosity (index * 10 + 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// AlertLog implementation
|
||||
|
||||
AlertLogButton::AlertLogButton (QWidget *parent)
|
||||
: QToolButton (parent)
|
||||
{
|
||||
mp_logger = new LogViewerDialog (this, false, false);
|
||||
hide ();
|
||||
connect (&mp_logger->file (), SIGNAL (attention_changed (bool)), this, SLOT (attention_changed (bool)));
|
||||
connect (this, SIGNAL (clicked ()), mp_logger, SLOT (exec ()));
|
||||
}
|
||||
|
||||
void
|
||||
AlertLogButton::attention_changed (bool attn)
|
||||
{
|
||||
setVisible (attn);
|
||||
|
||||
// as a special service, enlarge and color any surrounding frame red -
|
||||
// this feature allows putting the alert button together with other entry fields into a frame and
|
||||
// make this frame highlighted on error or warning.
|
||||
QFrame *frame = dynamic_cast<QFrame *> (parent ());
|
||||
if (frame) {
|
||||
|
||||
if (frame->layout ()) {
|
||||
int l = 0, t = 0, r = 0, b = 0;
|
||||
frame->layout ()->getContentsMargins (&l, &t, &r, &b);
|
||||
if (attn) {
|
||||
l += 3; t += 3; r += 2; b += 2;
|
||||
} else {
|
||||
l -= 3; t -= 3; r -= 2; b -= 2;
|
||||
}
|
||||
frame->layout ()->setContentsMargins (l, t, r, b);
|
||||
}
|
||||
|
||||
if (attn) {
|
||||
|
||||
frame->setAutoFillBackground (true);
|
||||
QPalette palette = frame->palette ();
|
||||
palette.setColor (QPalette::Window, QColor (255, 160, 160));
|
||||
frame->setPalette (palette);
|
||||
|
||||
} else {
|
||||
|
||||
frame->setAutoFillBackground (false);
|
||||
frame->setPalette (QPalette ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@
|
|||
|
||||
#include "ui_LogViewerDialog.h"
|
||||
#include "tlLog.h"
|
||||
#include "layCommon.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QDialog>
|
||||
#include <QAbstractListModel>
|
||||
#include <QToolButton>
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
|
@ -40,6 +42,9 @@ namespace lay
|
|||
|
||||
class LogFile;
|
||||
|
||||
/**
|
||||
* @brief A helper class describing one log entry
|
||||
*/
|
||||
class LogFileEntry
|
||||
{
|
||||
public:
|
||||
|
|
@ -70,7 +75,10 @@ private:
|
|||
bool m_continued;
|
||||
};
|
||||
|
||||
class LogReceiver
|
||||
/**
|
||||
* @brief The log receiver abstraction that connects a channel with the LogFile object
|
||||
*/
|
||||
class LAY_PUBLIC LogReceiver
|
||||
: public tl::Channel
|
||||
{
|
||||
public:
|
||||
|
|
@ -91,40 +99,110 @@ private:
|
|||
QMutex m_lock;
|
||||
};
|
||||
|
||||
class LogFile
|
||||
/**
|
||||
* @brief A log collection ("log file")
|
||||
*
|
||||
* The log collector collects warnings, errors and info messages
|
||||
* and presents this collection as a QAbstractListModel view
|
||||
* viewing inside a QTreeWidget or the LogViewerDialog.
|
||||
*
|
||||
* The log collector can either be used standalone or as a
|
||||
* global receiver that will collect the global log
|
||||
* messages.
|
||||
*/
|
||||
class LAY_PUBLIC LogFile
|
||||
: public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LogFile (size_t max_entries);
|
||||
|
||||
void error (const std::string &msg, bool continued)
|
||||
{
|
||||
add (continued ? LogFileEntry::ErrorContinued : LogFileEntry::Error, msg, continued);
|
||||
}
|
||||
|
||||
void info (const std::string &msg, bool continued)
|
||||
{
|
||||
add (continued ? LogFileEntry::InfoContinued : LogFileEntry::Info, msg, continued);
|
||||
}
|
||||
|
||||
void warn (const std::string &msg, bool continued)
|
||||
{
|
||||
add (continued ? LogFileEntry::WarningContinued : LogFileEntry::Warning, msg, continued);
|
||||
}
|
||||
/**
|
||||
* @brief Constructs a log file receiver
|
||||
* If "register_global" is true, the receiver will register itself as a global log receiver.
|
||||
* Otherwise it's a private one that can be used with the "error", "warn" and "info" channels
|
||||
* provided by the respective methods.
|
||||
*/
|
||||
LogFile (size_t max_entries, bool register_global = true);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
|
||||
private slots:
|
||||
void timeout ();
|
||||
/**
|
||||
* @brief Gets a value indicating whether errors are present
|
||||
*/
|
||||
bool has_errors () const;
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether warnings are present
|
||||
*/
|
||||
bool has_warnings () const;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Clears the log
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Adds a separator
|
||||
*/
|
||||
void separator ();
|
||||
|
||||
/**
|
||||
* @brief copies the contents to the clipboard
|
||||
*/
|
||||
void copy ();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Gets the error channel
|
||||
*/
|
||||
tl::Channel &error ()
|
||||
{
|
||||
return m_error_receiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the warning channel
|
||||
*/
|
||||
tl::Channel &warn ()
|
||||
{
|
||||
return m_warn_receiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the info channel
|
||||
*/
|
||||
tl::Channel &info ()
|
||||
{
|
||||
return m_info_receiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the log channel
|
||||
*/
|
||||
tl::Channel &log ()
|
||||
{
|
||||
return m_log_receiver;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void timeout ();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief This signal is emitted if the log's attention state has changed
|
||||
* Attention state is "true" if either errors or warnings are present.
|
||||
*/
|
||||
void attention_changed (bool f);
|
||||
|
||||
private:
|
||||
QTimer m_timer;
|
||||
mutable QMutex m_lock;
|
||||
LogReceiver m_error_receiver;
|
||||
|
|
@ -135,18 +213,63 @@ public:
|
|||
size_t m_max_entries;
|
||||
size_t m_generation_id;
|
||||
size_t m_last_generation_id;
|
||||
bool m_has_errors, m_has_warnings;
|
||||
bool m_last_attn;
|
||||
|
||||
/**
|
||||
* @brief Adds an error
|
||||
*/
|
||||
void add_error (const std::string &msg, bool continued)
|
||||
{
|
||||
add (continued ? LogFileEntry::ErrorContinued : LogFileEntry::Error, msg, continued);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a info message
|
||||
*/
|
||||
void add_info (const std::string &msg, bool continued)
|
||||
{
|
||||
add (continued ? LogFileEntry::InfoContinued : LogFileEntry::Info, msg, continued);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a warning
|
||||
*/
|
||||
void add_warn (const std::string &msg, bool continued)
|
||||
{
|
||||
add (continued ? LogFileEntry::WarningContinued : LogFileEntry::Warning, msg, continued);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds anything
|
||||
*/
|
||||
void add (LogFileEntry::mode_type mode, const std::string &msg, bool continued);
|
||||
};
|
||||
|
||||
class LogViewerDialog
|
||||
/**
|
||||
* @brief A dialog presenting the log file
|
||||
*/
|
||||
class LAY_PUBLIC LogViewerDialog
|
||||
: public QDialog,
|
||||
public Ui::LogViewerDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LogViewerDialog (QWidget *parent);
|
||||
/**
|
||||
* @brief The constructor
|
||||
* If "register_global" is true, the log is registered globally
|
||||
* and will receiver global log messages.
|
||||
*/
|
||||
LogViewerDialog (QWidget *parent, bool register_global = true, bool interactive = true);
|
||||
|
||||
/**
|
||||
* @brief Gets the log file object
|
||||
*/
|
||||
LogFile &file ()
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void verbosity_changed (int l);
|
||||
|
|
@ -155,7 +278,93 @@ private:
|
|||
LogFile m_file;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A tool button that collects logs and makes itself visible once attention is required
|
||||
*/
|
||||
class LAY_PUBLIC AlertLogButton
|
||||
: public QToolButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
AlertLogButton (QWidget *parent);
|
||||
|
||||
/**
|
||||
* @brief Gets the error channel
|
||||
*/
|
||||
tl::Channel &error () const
|
||||
{
|
||||
return mp_logger->file ().error ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the warn channel
|
||||
*/
|
||||
tl::Channel &warn () const
|
||||
{
|
||||
return mp_logger->file ().warn ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the info channel
|
||||
*/
|
||||
tl::Channel &info () const
|
||||
{
|
||||
return mp_logger->file ().info ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the log channel
|
||||
*/
|
||||
tl::Channel &log () const
|
||||
{
|
||||
return mp_logger->file ().log ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the error status of the log
|
||||
*/
|
||||
bool has_errors () const
|
||||
{
|
||||
return mp_logger->file ().has_errors ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the warning status of the log
|
||||
*/
|
||||
bool has_warnings () const
|
||||
{
|
||||
return mp_logger->file ().has_warnings ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the attention status of the log
|
||||
* (either warnings or errors are present)
|
||||
*/
|
||||
bool needs_attention () const
|
||||
{
|
||||
return has_errors () || has_warnings ();
|
||||
}
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Clears the log (and makes the button invisible)
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
mp_logger->file ().clear ();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void attention_changed (bool);
|
||||
|
||||
private:
|
||||
LogViewerDialog *mp_logger;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@
|
|||
#include "layLogViewerDialog.h"
|
||||
#include "layLayerToolbox.h"
|
||||
#include "laySettingsForm.h"
|
||||
#include "laySettingsForm.h"
|
||||
#include "layTechnologyController.h"
|
||||
#include "laySaltManagerDialog.h"
|
||||
#include "layTipDialog.h"
|
||||
#include "laySelectCellViewForm.h"
|
||||
#include "layLayoutPropertiesForm.h"
|
||||
|
|
@ -928,6 +928,7 @@ MainWindow::init_menu ()
|
|||
};
|
||||
|
||||
MenuLayoutEntry tools_menu [] = {
|
||||
MenuLayoutEntry ("packages", tl::to_string (QObject::tr ("Manage Packages")), SLOT (cm_packages ())),
|
||||
MenuLayoutEntry ("technologies", tl::to_string (QObject::tr ("Manage Technologies")), SLOT (cm_technologies ())),
|
||||
MenuLayoutEntry::separator ("verification_group"),
|
||||
MenuLayoutEntry ("drc", tl::to_string (QObject::tr ("DRC")), drc_menu),
|
||||
|
|
@ -4509,6 +4510,13 @@ MainWindow::show_progress_bar (bool show)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::cm_packages ()
|
||||
{
|
||||
lay::SaltManagerDialog dialog (this);
|
||||
dialog.exec ();
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::cm_technologies ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -720,6 +720,7 @@ public slots:
|
|||
void cm_macro_editor ();
|
||||
void cm_new_drc_script ();
|
||||
void cm_edit_drc_scripts ();
|
||||
void cm_packages ();
|
||||
void cm_technologies ();
|
||||
void cm_open_too ();
|
||||
void cm_open_new_view ();
|
||||
|
|
|
|||
|
|
@ -113,8 +113,18 @@
|
|||
<file alias="upup.png">images/upup.png</file>
|
||||
<file alias="waived.png">images/waived.png</file>
|
||||
<file alias="yellow_flag.png">images/yellow_flag.png</file>
|
||||
<file alias="salt.png">images/salt.png</file>
|
||||
<file alias="salt_icon.png">images/salt_icon.png</file>
|
||||
<file alias="warn.png">images/warn.png</file>
|
||||
<file alias="warn_16.png">images/warn_16.png</file>
|
||||
<file alias="empty_16.png">images/empty_16.png</file>
|
||||
<file alias="error_16.png">images/error_16.png</file>
|
||||
<file alias="info_16.png">images/info_16.png</file>
|
||||
<file alias="marked_24.png">images/marked_24.png</file>
|
||||
<file alias="marked_64.png">images/marked_64.png</file>
|
||||
<file alias="marked_16.png">images/marked_16.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/syntax" >
|
||||
<qresource prefix="/syntax">
|
||||
<file alias="ruby.xml">syntax/ruby.xml</file>
|
||||
<file alias="python.xml">syntax/python.xml</file>
|
||||
</qresource>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,427 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySalt.h"
|
||||
#include "tlString.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlWebDAV.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QResource>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
Salt::Salt ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Salt::Salt (const Salt &other)
|
||||
: QObject ()
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
Salt &Salt::operator= (const Salt &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_root = other.m_root;
|
||||
invalidate ();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Salt::flat_iterator
|
||||
Salt::begin_flat ()
|
||||
{
|
||||
validate ();
|
||||
return mp_flat_grains.begin ();
|
||||
}
|
||||
|
||||
Salt::flat_iterator
|
||||
Salt::end_flat ()
|
||||
{
|
||||
validate ();
|
||||
return mp_flat_grains.end ();
|
||||
}
|
||||
|
||||
SaltGrain *
|
||||
Salt::grain_by_name (const std::string &name)
|
||||
{
|
||||
validate ();
|
||||
std::map<std::string, SaltGrain *>::const_iterator g = m_grains_by_name.find (name);
|
||||
if (g != m_grains_by_name.end ()) {
|
||||
return g->second;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Salt::add_location (const std::string &path)
|
||||
{
|
||||
tl_assert (! path.empty ());
|
||||
|
||||
if (path[0] != ':') {
|
||||
// do nothing if the collection is already there
|
||||
QFileInfo fi (tl::to_qstring (path));
|
||||
for (lay::SaltGrains::collection_iterator g = m_root.begin_collections (); g != m_root.end_collections (); ++g) {
|
||||
if (QFileInfo (tl::to_qstring (g->path ())) == fi) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lay::SaltGrains gg = lay::SaltGrains::from_path (path);
|
||||
m_root.add_collection (gg);
|
||||
invalidate ();
|
||||
}
|
||||
|
||||
void
|
||||
Salt::remove_location (const std::string &path)
|
||||
{
|
||||
QFileInfo fi (tl::to_qstring (path));
|
||||
for (lay::SaltGrains::collection_iterator g = m_root.begin_collections (); g != m_root.end_collections (); ++g) {
|
||||
if (QFileInfo (tl::to_qstring (g->path ())) == fi) {
|
||||
m_root.remove_collection (g, false);
|
||||
invalidate ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Salt::refresh ()
|
||||
{
|
||||
lay::SaltGrains new_root;
|
||||
for (lay::SaltGrains::collection_iterator g = m_root.begin_collections (); g != m_root.end_collections (); ++g) {
|
||||
new_root.add_collection (lay::SaltGrains::from_path (g->path ()));
|
||||
}
|
||||
if (new_root != m_root) {
|
||||
m_root = new_root;
|
||||
invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Salt::add_collection_to_flat (SaltGrains &gg)
|
||||
{
|
||||
for (lay::SaltGrains::grain_iterator g = gg.begin_grains (); g != gg.end_grains (); ++g) {
|
||||
// TODO: get rid of the const cast - would require a non-const grain iterator
|
||||
mp_flat_grains.push_back (const_cast <SaltGrain *> (g.operator-> ()));
|
||||
}
|
||||
for (lay::SaltGrains::collection_iterator g = gg.begin_collections (); g != gg.end_collections (); ++g) {
|
||||
// TODO: get rid of the const cast - would require a non-const grain collection iterator
|
||||
add_collection_to_flat (const_cast <SaltGrains &> (*g));
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct NameCompare
|
||||
{
|
||||
bool operator () (lay::SaltGrain *a, lay::SaltGrain *b) const
|
||||
{
|
||||
// TODO: UTF-8 support?
|
||||
return a->name () < b->name ();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Salt::validate ()
|
||||
{
|
||||
if (mp_flat_grains.empty ()) {
|
||||
|
||||
add_collection_to_flat (m_root);
|
||||
|
||||
m_grains_by_name.clear ();
|
||||
for (std::vector<SaltGrain *>::const_iterator i = mp_flat_grains.begin (); i != mp_flat_grains.end (); ++i) {
|
||||
m_grains_by_name.insert (std::make_pair ((*i)->name (), *i));
|
||||
}
|
||||
|
||||
// NOTE: we intentionally sort after the name list has been built - this way
|
||||
// the first entry will win in the name to grain map.
|
||||
std::sort (mp_flat_grains.begin (), mp_flat_grains.end (), NameCompare ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Salt::invalidate ()
|
||||
{
|
||||
mp_flat_grains.clear ();
|
||||
emit collections_changed ();
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
bool remove_from_collection (SaltGrains &collection, const std::string &name)
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
for (SaltGrains::grain_iterator g = collection.begin_grains (); g != collection.end_grains (); ++g) {
|
||||
if (g->name () == name) {
|
||||
SaltGrains::grain_iterator gnext = g;
|
||||
++gnext;
|
||||
collection.remove_grain (g, true);
|
||||
res = true;
|
||||
g = gnext;
|
||||
}
|
||||
}
|
||||
|
||||
for (SaltGrains::collection_iterator gg = collection.begin_collections (); gg != collection.end_collections (); ++gg) {
|
||||
// TODO: remove this const_cast
|
||||
if (remove_from_collection (const_cast <SaltGrains &> (*gg), name)) {
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
Salt::remove_grain (const SaltGrain &grain)
|
||||
{
|
||||
tl::info << QObject::tr ("Removing package '%1' ..").arg (tl::to_qstring (grain.name ()));
|
||||
if (remove_from_collection (m_root, grain.name ())) {
|
||||
|
||||
tl::info << QObject::tr ("Package '%1' removed.").arg (tl::to_qstring (grain.name ()));
|
||||
|
||||
// NOTE: this is a bit brute force .. we could as well try to insert the new grain into the existing structure
|
||||
refresh ();
|
||||
|
||||
invalidate ();
|
||||
return true;
|
||||
|
||||
} else {
|
||||
tl::warn << QObject::tr ("Failed to remove package '%1'.").arg (tl::to_qstring (grain.name ()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A helper class required because directory traversal is not supported by QResource directly
|
||||
* This class supports resource file trees and extraction of a tree from the latter
|
||||
*/
|
||||
class ResourceDir
|
||||
: public QResource
|
||||
{
|
||||
public:
|
||||
using QResource::isFile;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* Creates a resource representing a resource tree.
|
||||
*/
|
||||
ResourceDir (const QString &path)
|
||||
: QResource (path)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes the resource tree to the target directory
|
||||
* Returns false on error - see log in this case.
|
||||
*/
|
||||
bool copy_to (const QDir &target)
|
||||
{
|
||||
if (isDir ()) {
|
||||
|
||||
QStringList templ_dir = children ();
|
||||
for (QStringList::const_iterator t = templ_dir.begin (); t != templ_dir.end (); ++t) {
|
||||
|
||||
ResourceDir child_res (fileName () + QString::fromUtf8 ("/") + *t);
|
||||
if (child_res.isFile ()) {
|
||||
|
||||
QFile file (target.absoluteFilePath (*t));
|
||||
if (! file.open (QIODevice::WriteOnly)) {
|
||||
tl::error << QObject::tr ("Unable to open target file for writing: %1").arg (target.absoluteFilePath (*t));
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data;
|
||||
if (child_res.isCompressed ()) {
|
||||
data = qUncompress ((const unsigned char *)child_res.data (), (int)child_res.size ());
|
||||
} else {
|
||||
data = QByteArray ((const char *)child_res.data (), (int)child_res.size ());
|
||||
}
|
||||
|
||||
file.write (data);
|
||||
|
||||
file.close ();
|
||||
|
||||
} else {
|
||||
|
||||
QFileInfo child_dir (target.absoluteFilePath (*t));
|
||||
if (! child_dir.exists ()) {
|
||||
if (! target.mkdir (*t)) {
|
||||
tl::error << QObject::tr ("Unable to create target directory: %1").arg (child_dir.path ());
|
||||
return false;
|
||||
}
|
||||
} else if (! child_dir.isDir ()) {
|
||||
tl::error << QObject::tr ("Unable to create target directory (is a file already): %1").arg (child_dir.path ());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! child_res.copy_to (QDir (target.absoluteFilePath (*t)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
Salt::create_grain (const SaltGrain &templ, SaltGrain &target)
|
||||
{
|
||||
tl_assert (!m_root.is_empty ());
|
||||
|
||||
const SaltGrains *coll = m_root.begin_collections ().operator-> ();
|
||||
|
||||
if (target.name ().empty ()) {
|
||||
target.set_name (templ.name ());
|
||||
}
|
||||
|
||||
if (target.path ().empty ()) {
|
||||
lay::SaltGrain *g = grain_by_name (target.name ());
|
||||
if (g) {
|
||||
target.set_path (g->path ());
|
||||
}
|
||||
}
|
||||
|
||||
std::string path = target.path ();
|
||||
if (! path.empty ()) {
|
||||
coll = 0;
|
||||
for (SaltGrains::collection_iterator gg = m_root.begin_collections (); gg != m_root.end_collections (); ++gg) {
|
||||
if (tl::is_parent_path (tl::to_qstring (gg->path ()), tl::to_qstring (path))) {
|
||||
coll = gg.operator-> ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
tl_assert (coll != 0);
|
||||
}
|
||||
|
||||
tl::info << QObject::tr ("Installing package '%1' ..").arg (tl::to_qstring (target.name ()));
|
||||
|
||||
QDir target_dir (tl::to_qstring (coll->path ()));
|
||||
|
||||
try {
|
||||
|
||||
// change down to the desired target location and create the directory structure while doing so
|
||||
std::vector<std::string> name_parts = tl::split (target.name (), "/");
|
||||
for (std::vector<std::string>::const_iterator n = name_parts.begin (); n != name_parts.end (); ++n) {
|
||||
|
||||
QFileInfo subdir (target_dir.filePath (tl::to_qstring (*n)));
|
||||
if (subdir.exists () && ! subdir.isDir ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Unable to create target directory '%1' for installing package - is already a file").arg (subdir.path ())));
|
||||
} else if (! subdir.exists ()) {
|
||||
if (! target_dir.mkdir (tl::to_qstring (*n))) {
|
||||
throw tl::Exception (tl::to_string (tr ("Unable to create target directory '%1' for installing package").arg (subdir.path ())));
|
||||
}
|
||||
if (! target_dir.cd (tl::to_qstring (*n))) {
|
||||
throw tl::Exception (tl::to_string (tr ("Unable to change to target directory '%1' for installing package").arg (subdir.path ())));
|
||||
}
|
||||
} else {
|
||||
if (! target_dir.cd (tl::to_qstring (*n))) {
|
||||
throw tl::Exception (tl::to_string (tr ("Unable to change to target directory '%1' for installing package").arg (subdir.path ())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
|
||||
std::string target_name = target.name ();
|
||||
target = templ;
|
||||
target.set_path (tl::to_string (target_dir.absolutePath ()));
|
||||
target.set_name (target_name);
|
||||
|
||||
if (! templ.path ().empty ()) {
|
||||
|
||||
if (templ.path ()[0] != ':') {
|
||||
|
||||
// if the template represents an actual folder, use the files from there
|
||||
tl::info << QObject::tr ("Copying package from '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ()));
|
||||
res = tl::cp_dir_recursive (templ.path (), target.path ());
|
||||
|
||||
} else {
|
||||
|
||||
// if the template represents a resource path, use the files from there
|
||||
tl::info << QObject::tr ("Installing package from resource '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ()));
|
||||
res = ResourceDir (tl::to_qstring (templ.path ())).copy_to (QDir (tl::to_qstring (target.path ())));
|
||||
|
||||
}
|
||||
|
||||
} else if (! templ.url ().empty ()) {
|
||||
|
||||
// otherwise download from the URL
|
||||
tl::info << QObject::tr ("Downloading package from '%1' to '%2' ..").arg (tl::to_qstring (templ.url ())).arg (tl::to_qstring (target.path ()));
|
||||
res = tl::WebDAVObject::download (templ.url (), target.path ());
|
||||
|
||||
target.set_url (templ.url ());
|
||||
|
||||
}
|
||||
|
||||
if (res) {
|
||||
|
||||
tl::info << QObject::tr ("Package '%1' installed").arg (tl::to_qstring (target.name ()));
|
||||
target.set_installed_time (QDateTime::currentDateTime ());
|
||||
target.save ();
|
||||
|
||||
// NOTE: this is a bit brute force .. we could as well try to insert the new grain into the existing structure
|
||||
refresh ();
|
||||
|
||||
} else {
|
||||
|
||||
tl::warn << QObject::tr ("Failed to install package '%1' - removing files ..").arg (tl::to_qstring (target.name ()));
|
||||
if (! tl::rm_dir_recursive (target.path ())) {
|
||||
tl::warn << QObject::tr ("Failed to remove files").arg (tl::to_qstring (target.name ()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySalt
|
||||
#define HDR_laySalt
|
||||
|
||||
#include "layCommon.h"
|
||||
#include "laySaltGrain.h"
|
||||
#include "laySaltGrains.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The global salt (package manager) object
|
||||
* This object can be configured to represent a couple of locations.
|
||||
* It will provide a collection of grains for these locations.
|
||||
*/
|
||||
class LAY_PUBLIC Salt
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
typedef SaltGrains::collection_iterator iterator;
|
||||
typedef std::vector<SaltGrain *>::const_iterator flat_iterator;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
Salt ();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
Salt (const Salt &other);
|
||||
|
||||
/**
|
||||
* @brief assignment
|
||||
*/
|
||||
Salt &operator= (const Salt &other);
|
||||
|
||||
/**
|
||||
* @brief Adds the given location to the ones the package manager uses
|
||||
* Adding a location will scan the folder and make the contents available
|
||||
* as a new collection.
|
||||
*/
|
||||
void add_location (const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Removes a given location
|
||||
* This will remove the collection from the package locations.
|
||||
*/
|
||||
void remove_location (const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Refreshes the collections
|
||||
* This method rescans all registered locations.
|
||||
*/
|
||||
void refresh ();
|
||||
|
||||
/**
|
||||
* @brief Iterates the collections (begin)
|
||||
*/
|
||||
iterator begin () const
|
||||
{
|
||||
return m_root.begin_collections ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates the collections (end)
|
||||
*/
|
||||
iterator end () const
|
||||
{
|
||||
return m_root.end_collections ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the collection is empty
|
||||
*/
|
||||
bool is_empty () const
|
||||
{
|
||||
return m_root.is_empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A flat iterator of (sorted) grains (begin)
|
||||
*/
|
||||
flat_iterator begin_flat ();
|
||||
|
||||
/**
|
||||
* @brief A flat iterator of (sorted) grains (end)
|
||||
*/
|
||||
flat_iterator end_flat ();
|
||||
|
||||
/**
|
||||
* @brief Gets the grain with the given name
|
||||
*/
|
||||
SaltGrain *grain_by_name (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the grain with the given name (const version)
|
||||
*/
|
||||
const SaltGrain *grain_by_name (const std::string &name) const
|
||||
{
|
||||
return const_cast<Salt *> (this)->grain_by_name (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Loads the salt from a "salt mine" file
|
||||
*/
|
||||
void load (const std::string &p)
|
||||
{
|
||||
m_root.load (p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Loads the salt from a "salt mine" stream
|
||||
*/
|
||||
void load (tl::InputStream &s)
|
||||
{
|
||||
m_root.load (s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Saves the salt to a "salt mine" file
|
||||
* This feature is provided for debugging purposes mainly.
|
||||
*/
|
||||
void save (const std::string &p)
|
||||
{
|
||||
m_root.save (p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes a grain from the salt
|
||||
*
|
||||
* This operation will remove the grain with the given name from the salt and delete all files and directories related to it.
|
||||
* If multiple grains with the same name exist, they will all be removed.
|
||||
*
|
||||
* Returns true, if the package could be removed successfully.
|
||||
*/
|
||||
bool remove_grain (const SaltGrain &grain);
|
||||
|
||||
/**
|
||||
* @brief Creates a new grain from a template
|
||||
*
|
||||
* This method will create a folder for a grain with the given path and download or copy
|
||||
* all files related to this grain. It will copy the download URL from the template into the
|
||||
* new grain, so updates will come from the original location.
|
||||
*
|
||||
* If the target's name is not set, it will be taken from the template.
|
||||
* If the target's path is not set and a grain with the given name already exists in
|
||||
* the package, the path is taken from that grain.
|
||||
* If no target path is set and no grain with this name exists yet, a new path will
|
||||
* be constructed using the first location in the salt.
|
||||
*
|
||||
* The target grain will be updated with the installation information. If the target grain
|
||||
* contains an installation path prior to the installation, this path will be used for the
|
||||
* installation of the grain files.
|
||||
*
|
||||
* Returns true, if the package could be created successfully.
|
||||
*/
|
||||
bool create_grain (const SaltGrain &templ, SaltGrain &target);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief A signal triggered when one of the collections changed
|
||||
*/
|
||||
void collections_changed ();
|
||||
|
||||
private:
|
||||
SaltGrains m_root;
|
||||
std::vector<SaltGrain *> mp_flat_grains;
|
||||
std::map<std::string, SaltGrain *> m_grains_by_name;
|
||||
|
||||
void validate ();
|
||||
void invalidate ();
|
||||
void add_collection_to_flat (lay::SaltGrains &gg);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltDownloadManager.h"
|
||||
#include "laySalt.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlWebDAV.h"
|
||||
|
||||
#include "ui_SaltManagerInstallConfirmationDialog.h"
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
class ConfirmationDialog
|
||||
: public QDialog, private Ui::SaltManagerInstallConfirmationDialog
|
||||
{
|
||||
public:
|
||||
ConfirmationDialog (QWidget *parent)
|
||||
: QDialog (parent)
|
||||
{
|
||||
Ui::SaltManagerInstallConfirmationDialog::setupUi (this);
|
||||
}
|
||||
|
||||
void add_info (const std::string &name, bool update, const std::string &version, const std::string &url)
|
||||
{
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem (list);
|
||||
|
||||
item->setFlags (item->flags () & ~Qt::ItemIsSelectable);
|
||||
|
||||
item->setText (0, tl::to_qstring (name));
|
||||
item->setText (1, update ? tr ("UPDATE") : tr ("INSTALL"));
|
||||
item->setText (2, tl::to_qstring (version));
|
||||
item->setText (3, tl::to_qstring (url));
|
||||
|
||||
for (int column = 0; column < list->colorCount (); ++column) {
|
||||
item->setData (column, Qt::ForegroundRole, update ? Qt::blue : Qt::black);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
SaltDownloadManager::SaltDownloadManager ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
SaltDownloadManager::register_download (const std::string &name, const std::string &url, const std::string &version)
|
||||
{
|
||||
m_registry.insert (std::make_pair (name, Descriptor (url, version)));
|
||||
}
|
||||
|
||||
void
|
||||
SaltDownloadManager::compute_dependencies (const lay::Salt &salt, const lay::Salt &salt_mine)
|
||||
{
|
||||
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Computing package dependencies ..")));
|
||||
|
||||
std::map<std::string, Descriptor> registry;
|
||||
|
||||
// remove those registered entries which don't need to be updated
|
||||
|
||||
registry = m_registry;
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
|
||||
const SaltGrain *g = salt.grain_by_name (p->first);
|
||||
if (g && SaltGrain::compare_versions (p->second.version, g->version ()) == 0 && p->second.url == g->url ()) {
|
||||
m_registry.erase (p->first);
|
||||
}
|
||||
}
|
||||
|
||||
// add further entries as derived from the dependencies
|
||||
|
||||
while (needs_iteration ()) {
|
||||
|
||||
fetch_missing (salt_mine, progress);
|
||||
|
||||
registry = m_registry;
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
|
||||
|
||||
for (std::vector<SaltGrain::Dependency>::const_iterator d = p->second.grain.dependencies ().begin (); d != p->second.grain.dependencies ().end (); ++d) {
|
||||
|
||||
std::map<std::string, Descriptor>::iterator r = m_registry.find (d->name);
|
||||
if (r != m_registry.end ()) {
|
||||
|
||||
if (SaltGrain::compare_versions (r->second.version, d->version) < 0) {
|
||||
|
||||
// Grain is present, but too old -> update version and reload in the next iteration
|
||||
r->second.downloaded = false;
|
||||
r->second.version = d->version;
|
||||
r->second.url = d->url;
|
||||
r->second.downloaded = false;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const SaltGrain *g = salt.grain_by_name (d->name);
|
||||
if (g) {
|
||||
|
||||
// Grain is installed already, but too old -> register for update
|
||||
if (SaltGrain::compare_versions (g->version (), d->version) < 0) {
|
||||
register_download (d->name, d->url, d->version);
|
||||
}
|
||||
|
||||
} else {
|
||||
register_download (d->name, d->url, d->version);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SaltDownloadManager::needs_iteration ()
|
||||
{
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
if (! p->second.downloaded) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SaltDownloadManager::fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProgress &progress)
|
||||
{
|
||||
for (std::map<std::string, Descriptor>::iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
|
||||
if (! p->second.downloaded) {
|
||||
|
||||
++progress;
|
||||
|
||||
// If no URL is given, utilize the salt mine to fetch it
|
||||
if (p->second.url.empty ()) {
|
||||
const lay::SaltGrain *g = salt_mine.grain_by_name (p->first);
|
||||
if (SaltGrain::compare_versions (g->version (), p->second.version) < 0) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Package '%1': package in repository is too old (%2) to satisfy requirements (%3)").arg (tl::to_qstring (p->first)).arg (tl::to_qstring (g->version ())).arg (tl::to_qstring (p->second.version))));
|
||||
}
|
||||
p->second.version = g->version ();
|
||||
p->second.url = g->url ();
|
||||
}
|
||||
|
||||
try {
|
||||
p->second.grain = SaltGrain::from_url (p->second.url);
|
||||
} catch (tl::Exception &ex) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Error fetching spec file for package '%1': %2").arg (tl::to_qstring (p->first)).arg (tl::to_qstring (ex.msg ()))));
|
||||
}
|
||||
|
||||
p->second.downloaded = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SaltDownloadManager::show_confirmation_dialog (QWidget *parent, const lay::Salt &salt)
|
||||
{
|
||||
// Stop with a warning if there is nothing to do
|
||||
if (m_registry.empty()) {
|
||||
QMessageBox::warning (parent, tr ("Nothing to do"), tr ("No packages need update or are marked for installation"));
|
||||
return false;
|
||||
}
|
||||
|
||||
lay::ConfirmationDialog dialog (parent);
|
||||
|
||||
// First the packages to update
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
const lay::SaltGrain *g = salt.grain_by_name (p->first);
|
||||
if (g) {
|
||||
// \342\206\222 is UTF-8 "right arrow"
|
||||
dialog.add_info (p->first, true, g->version () + " \342\206\222 " + p->second.version, p->second.url);
|
||||
}
|
||||
}
|
||||
|
||||
// Then the packages to install
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
const lay::SaltGrain *g = salt.grain_by_name (p->first);
|
||||
if (!g) {
|
||||
dialog.add_info (p->first, false, p->second.version, p->second.url);
|
||||
}
|
||||
}
|
||||
|
||||
return dialog.exec ();
|
||||
}
|
||||
|
||||
bool
|
||||
SaltDownloadManager::execute (lay::Salt &salt)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Downloading packages")), m_registry.size (), 1);
|
||||
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
|
||||
lay::SaltGrain target;
|
||||
target.set_name (p->first);
|
||||
lay::SaltGrain *g = salt.grain_by_name (p->first);
|
||||
if (g) {
|
||||
target.set_path (g->path ());
|
||||
}
|
||||
|
||||
if (! salt.create_grain (p->second.grain, target)) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
++progress;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySaltDownloadManager
|
||||
#define HDR_laySaltDownloadManager
|
||||
|
||||
#include "layCommon.h"
|
||||
#include "laySaltGrain.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class Salt;
|
||||
|
||||
/**
|
||||
* @brief The download manager
|
||||
*
|
||||
* This class is responsible for handling the downloads for
|
||||
* grains. The basic sequence is:
|
||||
* + "register_download" (multiple times) to register the packages intended for download
|
||||
* + "compute_dependencies" to determine all related packages
|
||||
* + (optional) "show_confirmation_dialog"
|
||||
* + "execute" to actually execute the downloads
|
||||
*/
|
||||
class LAY_PUBLIC SaltDownloadManager
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
SaltDownloadManager ();
|
||||
|
||||
/**
|
||||
* @brief Registers an URL (with version) for download in the given target directory
|
||||
*
|
||||
* The target directory can be empty. In this case, the downloader will pick an approriate one.
|
||||
*/
|
||||
void register_download (const std::string &name, const std::string &url, const std::string &version);
|
||||
|
||||
/**
|
||||
* @brief Computes the dependencies after all required packages have been registered
|
||||
*
|
||||
* This method will compute the dependencies. Packages not present in the list of
|
||||
* packages ("salt" argument), will be scheduled for download too. Dependency packages
|
||||
* are looked up in "salt_mine" if no download URL is given.
|
||||
*/
|
||||
void compute_dependencies (const lay::Salt &salt, const Salt &salt_mine);
|
||||
|
||||
/**
|
||||
* @brief Presents a dialog showing the packages scheduled for download
|
||||
*
|
||||
* This method requires all dependencies to be computed. It will return false
|
||||
* if the dialog is not confirmed.
|
||||
*
|
||||
* "salt" needs to be the currently installed packages so the dialog can
|
||||
* indicate which packages will be updated.
|
||||
*/
|
||||
bool show_confirmation_dialog (QWidget *parent, const lay::Salt &salt);
|
||||
|
||||
/**
|
||||
* @brief Actually execute the downloads
|
||||
*
|
||||
* This method will return false if anything goes wrong.
|
||||
* Failed packages will be removed entirely after they have been listed in
|
||||
* an error dialog.
|
||||
*/
|
||||
bool execute (lay::Salt &salt);
|
||||
|
||||
private:
|
||||
struct Descriptor
|
||||
{
|
||||
Descriptor (const std::string &_url, const std::string &_version)
|
||||
: url (_url), version (_version), downloaded (false)
|
||||
{ }
|
||||
|
||||
std::string url;
|
||||
std::string version;
|
||||
bool downloaded;
|
||||
lay::SaltGrain grain;
|
||||
};
|
||||
|
||||
std::map<std::string, Descriptor> m_registry;
|
||||
|
||||
bool needs_iteration ();
|
||||
void fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProgress &progress);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,422 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltGrain.h"
|
||||
#include "tlString.h"
|
||||
#include "tlXMLParser.h"
|
||||
#include "tlHttpStream.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QBuffer>
|
||||
#include <QResource>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
static const std::string grain_filename = "grain.xml";
|
||||
|
||||
SaltGrain::SaltGrain ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrain::operator== (const SaltGrain &other) const
|
||||
{
|
||||
return m_name == other.m_name &&
|
||||
m_path == other.m_path &&
|
||||
m_version == other.m_version &&
|
||||
m_url == other.m_url &&
|
||||
m_title == other.m_title &&
|
||||
m_doc == other.m_doc &&
|
||||
m_doc_url == other.m_doc_url &&
|
||||
m_icon == other.m_icon &&
|
||||
m_screenshot == other.m_screenshot &&
|
||||
m_dependencies == other.m_dependencies &&
|
||||
m_author == other.m_author &&
|
||||
m_author_contact == other.m_author_contact &&
|
||||
m_license == other.m_license &&
|
||||
m_authored_time == other.m_authored_time &&
|
||||
m_installed_time == other.m_installed_time;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_version (const std::string &v)
|
||||
{
|
||||
m_version = v;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_path (const std::string &p)
|
||||
{
|
||||
m_path = p;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_url (const std::string &u)
|
||||
{
|
||||
m_url = u;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_title (const std::string &t)
|
||||
{
|
||||
m_title = t;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_doc (const std::string &t)
|
||||
{
|
||||
m_doc = t;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_doc_url (const std::string &u)
|
||||
{
|
||||
m_doc_url = u;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_author (const std::string &a)
|
||||
{
|
||||
m_author = a;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_author_contact (const std::string &a)
|
||||
{
|
||||
m_author_contact = a;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_license (const std::string &l)
|
||||
{
|
||||
m_license = l;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_authored_time (const QDateTime &t)
|
||||
{
|
||||
m_authored_time = t;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_installed_time (const QDateTime &t)
|
||||
{
|
||||
m_installed_time = t;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_screenshot (const QImage &i)
|
||||
{
|
||||
m_screenshot = i;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::set_icon (const QImage &i)
|
||||
{
|
||||
m_icon = i;
|
||||
}
|
||||
|
||||
int
|
||||
SaltGrain::compare_versions (const std::string &v1, const std::string &v2)
|
||||
{
|
||||
tl::Extractor ex1 (v1.c_str ());
|
||||
tl::Extractor ex2 (v2.c_str ());
|
||||
|
||||
while (true) {
|
||||
|
||||
if (ex1.at_end () && ex2.at_end ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n1 = 0, n2 = 0;
|
||||
if (! ex1.at_end ()) {
|
||||
ex1.try_read (n1);
|
||||
}
|
||||
if (! ex2.at_end ()) {
|
||||
ex2.try_read (n2);
|
||||
}
|
||||
|
||||
if (n1 != n2) {
|
||||
return n1 < n2 ? -1 : 1;
|
||||
}
|
||||
|
||||
while (! ex1.at_end ()) {
|
||||
char c = *ex1;
|
||||
++ex1;
|
||||
if (c == '.') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (! ex2.at_end ()) {
|
||||
char c = *ex2;
|
||||
++ex2;
|
||||
if (c == '.') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
SaltGrain::spec_url (const std::string &url)
|
||||
{
|
||||
std::string res = url;
|
||||
if (! res.empty()) {
|
||||
if (res [res.size () - 1] != '/') {
|
||||
res += "/";
|
||||
}
|
||||
res += grain_filename;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrain::valid_name (const std::string &n)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
tl::Extractor ex (n);
|
||||
|
||||
std::string s;
|
||||
if (! ex.try_read_word (s, "_")) {
|
||||
return false;
|
||||
}
|
||||
res += s;
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
if (! ex.test ("/")) {
|
||||
return false;
|
||||
}
|
||||
if (! ex.try_read_word (s, "_")) {
|
||||
return false;
|
||||
}
|
||||
res += "/";
|
||||
res += s;
|
||||
}
|
||||
|
||||
// this captures the cases where the extractor skips blanks
|
||||
// TODO: the extractor should have a "non-blank-skipping" mode
|
||||
return res == n;
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrain::valid_version (const std::string &v)
|
||||
{
|
||||
tl::Extractor ex (v.c_str ());
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
int n = 0;
|
||||
if (! ex.try_read (n)) {
|
||||
return false;
|
||||
}
|
||||
if (! ex.at_end ()) {
|
||||
if (*ex != '.') {
|
||||
return false;
|
||||
} else {
|
||||
++ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct TimeConverter
|
||||
{
|
||||
std::string to_string (const QDateTime &time) const
|
||||
{
|
||||
if (time.isNull ()) {
|
||||
return std::string ();
|
||||
} else {
|
||||
return tl::to_string (time.toString (Qt::ISODate));
|
||||
}
|
||||
}
|
||||
|
||||
void from_string (const std::string &time, QDateTime &res) const
|
||||
{
|
||||
if (time.empty ()) {
|
||||
res = QDateTime ();
|
||||
} else {
|
||||
res = QDateTime::fromString (tl::to_qstring (time), Qt::ISODate);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ImageConverter
|
||||
{
|
||||
std::string to_string (const QImage &image) const
|
||||
{
|
||||
if (image.isNull ()) {
|
||||
return std::string ();
|
||||
} else {
|
||||
QBuffer buffer;
|
||||
buffer.open (QIODevice::WriteOnly);
|
||||
image.save (&buffer, "PNG");
|
||||
buffer.close ();
|
||||
return buffer.buffer ().toBase64 ().constData ();
|
||||
}
|
||||
}
|
||||
|
||||
void from_string (const std::string &image, QImage &res) const
|
||||
{
|
||||
if (image.empty ()) {
|
||||
res = QImage ();
|
||||
} else {
|
||||
res = QImage::fromData (QByteArray::fromBase64 (QByteArray (image.c_str (), image.size ())));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static tl::XMLElementList s_xml_elements =
|
||||
tl::make_member (&SaltGrain::name, &SaltGrain::set_name, "name") +
|
||||
tl::make_member (&SaltGrain::version, &SaltGrain::set_version, "version") +
|
||||
tl::make_member (&SaltGrain::title, &SaltGrain::set_title, "title") +
|
||||
tl::make_member (&SaltGrain::doc, &SaltGrain::set_doc, "doc") +
|
||||
tl::make_member (&SaltGrain::doc_url, &SaltGrain::set_doc_url, "doc-url") +
|
||||
tl::make_member (&SaltGrain::url, &SaltGrain::set_url, "url") +
|
||||
tl::make_member (&SaltGrain::license, &SaltGrain::set_license, "license") +
|
||||
tl::make_member (&SaltGrain::author, &SaltGrain::set_author, "author") +
|
||||
tl::make_member (&SaltGrain::author_contact, &SaltGrain::set_author_contact, "author-contact") +
|
||||
tl::make_member (&SaltGrain::authored_time, &SaltGrain::set_authored_time, "authored-time", TimeConverter ()) +
|
||||
tl::make_member (&SaltGrain::installed_time, &SaltGrain::set_installed_time, "installed-time", TimeConverter ()) +
|
||||
tl::make_member (&SaltGrain::icon, &SaltGrain::set_icon, "icon", ImageConverter ()) +
|
||||
tl::make_member (&SaltGrain::screenshot, &SaltGrain::set_screenshot, "screenshot", ImageConverter ()) +
|
||||
tl::make_element (&SaltGrain::begin_dependencies, &SaltGrain::end_dependencies, &SaltGrain::add_dependency, "depends",
|
||||
tl::make_member (&SaltGrain::Dependency::name, "name") +
|
||||
tl::make_member (&SaltGrain::Dependency::url, "url") +
|
||||
tl::make_member (&SaltGrain::Dependency::version, "version")
|
||||
);
|
||||
|
||||
static tl::XMLStruct<lay::SaltGrain> s_xml_struct ("salt-grain", s_xml_elements);
|
||||
|
||||
tl::XMLElementList &
|
||||
SaltGrain::xml_struct ()
|
||||
{
|
||||
return s_xml_elements;
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrain::is_readonly () const
|
||||
{
|
||||
return !QFileInfo (tl::to_qstring (path ())).isWritable ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::load (const std::string &p)
|
||||
{
|
||||
tl_assert (!p.empty ());
|
||||
|
||||
if (p[0] != ':') {
|
||||
|
||||
tl::XMLFileSource source (p);
|
||||
s_xml_struct.parse (source, *this);
|
||||
|
||||
} else {
|
||||
|
||||
QResource res (tl::to_qstring (p));
|
||||
QByteArray data;
|
||||
if (res.isCompressed ()) {
|
||||
data = qUncompress ((const unsigned char *)res.data (), (int)res.size ());
|
||||
} else {
|
||||
data = QByteArray ((const char *)res.data (), (int)res.size ());
|
||||
}
|
||||
|
||||
std::string str_data (data.constData (), data.size ());
|
||||
tl::XMLStringSource source (str_data);
|
||||
s_xml_struct.parse (source, *this);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::load (tl::InputStream &p)
|
||||
{
|
||||
tl::XMLStreamSource source (p);
|
||||
s_xml_struct.parse (source, *this);
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::save () const
|
||||
{
|
||||
save (tl::to_string (QDir (tl::to_qstring (path ())).filePath (tl::to_qstring (grain_filename))));
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrain::save (const std::string &p) const
|
||||
{
|
||||
tl::OutputStream os (p, tl::OutputStream::OM_Plain);
|
||||
s_xml_struct.write (os, *this);
|
||||
}
|
||||
|
||||
SaltGrain
|
||||
SaltGrain::from_path (const std::string &path)
|
||||
{
|
||||
QDir dir (tl::to_qstring (path));
|
||||
|
||||
SaltGrain g;
|
||||
g.load (tl::to_string (dir.filePath (tl::to_qstring (grain_filename))));
|
||||
g.set_path (tl::to_string (dir.absolutePath ()));
|
||||
return g;
|
||||
}
|
||||
|
||||
SaltGrain
|
||||
SaltGrain::from_url (const std::string &url)
|
||||
{
|
||||
if (url.empty ()) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("No download link available")));
|
||||
}
|
||||
|
||||
tl::InputHttpStream http (SaltGrain::spec_url (url));
|
||||
tl::InputStream stream (http);
|
||||
|
||||
SaltGrain g;
|
||||
g.load (stream);
|
||||
g.set_url (url);
|
||||
return g;
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrain::is_grain (const std::string &path)
|
||||
{
|
||||
tl_assert (! path.empty ());
|
||||
|
||||
if (path[0] != ':') {
|
||||
QDir dir (tl::to_qstring (path));
|
||||
QString gf = dir.filePath (tl::to_qstring (grain_filename));
|
||||
return QFileInfo (gf).exists ();
|
||||
} else {
|
||||
return QResource (tl::to_qstring (path + "/" + grain_filename)).isValid ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,420 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySaltGrain
|
||||
#define HDR_laySaltGrain
|
||||
|
||||
#include "layCommon.h"
|
||||
#include "tlObject.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlXMLParser.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QImage>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This class represents on grain of salt
|
||||
* "One grain of salt" is one package.
|
||||
*/
|
||||
class LAY_PUBLIC SaltGrain
|
||||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief A descriptor for one dependency
|
||||
* A dependency can be specified either through a name (see name property)
|
||||
* or a download URL. If download URL are specified, they have precedence
|
||||
* over names.
|
||||
* The version is the minimum required version. If empty, any version is
|
||||
* allowed to resolve this dependency.
|
||||
*/
|
||||
struct Dependency
|
||||
{
|
||||
std::string name;
|
||||
std::string url;
|
||||
std::string version;
|
||||
|
||||
bool operator== (const Dependency &other) const
|
||||
{
|
||||
return name == other.name && url == other.url && version == other.version;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SaltGrain ();
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const SaltGrain &other) const;
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
*/
|
||||
bool operator!= (const SaltGrain &other) const
|
||||
{
|
||||
return !operator== (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the grain
|
||||
*
|
||||
* The name is either a plain name (a word) or a path into a collection.
|
||||
* Name paths are formed using the "/" separator. "mypackage" is a plain name,
|
||||
* while "mycollection/mypackage" is a package within a collection. Collections
|
||||
* can be used to group packages. Names are case sensitive in general, but
|
||||
* names differing only in case should be avoided.
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the name of the grain
|
||||
*/
|
||||
void set_name (const std::string &p);
|
||||
|
||||
/**
|
||||
* @brief Gets the title of the grain
|
||||
*
|
||||
* The title is a brief description that is shown in the title of the
|
||||
* package manager.
|
||||
*/
|
||||
const std::string &title () const
|
||||
{
|
||||
return m_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the title of the grain
|
||||
*/
|
||||
void set_title (const std::string &t);
|
||||
|
||||
/**
|
||||
* @brief Gets the documentation text of the grain
|
||||
*
|
||||
* The documentation text is a brief description.
|
||||
*/
|
||||
const std::string &doc () const
|
||||
{
|
||||
return m_doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the documentation text of the grain
|
||||
*/
|
||||
void set_doc (const std::string &t);
|
||||
|
||||
/**
|
||||
* @brief Gets the documentation URL of the grain
|
||||
*
|
||||
* The documentation URL provides a detailed documentation.
|
||||
*/
|
||||
const std::string &doc_url () const
|
||||
{
|
||||
return m_doc_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the documentation URL of the grain
|
||||
*/
|
||||
void set_doc_url (const std::string &u);
|
||||
|
||||
/**
|
||||
* @brief Gets the version of the grain
|
||||
*
|
||||
* A version string is of the form "x.y..." where x, y and other version
|
||||
* components are integer numbers.
|
||||
*/
|
||||
const std::string &version () const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the version of the grain
|
||||
*/
|
||||
void set_version (const std::string &v);
|
||||
|
||||
/**
|
||||
* @brief Gets the author of the grain
|
||||
*/
|
||||
const std::string &author () const
|
||||
{
|
||||
return m_author;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the author of the grain
|
||||
*/
|
||||
void set_author (const std::string &a);
|
||||
|
||||
/**
|
||||
* @brief Gets the author's contact
|
||||
*/
|
||||
const std::string &author_contact () const
|
||||
{
|
||||
return m_author_contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the author's contact
|
||||
*/
|
||||
void set_author_contact (const std::string &a);
|
||||
|
||||
/**
|
||||
* @brief Gets the license of the grain
|
||||
*/
|
||||
const std::string &license () const
|
||||
{
|
||||
return m_license;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the license of the grain
|
||||
*/
|
||||
void set_license (const std::string &l);
|
||||
|
||||
/**
|
||||
* @brief Gets the release date and/or time of the grain
|
||||
*/
|
||||
const QDateTime &authored_time () const
|
||||
{
|
||||
return m_authored_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the release date and/or time
|
||||
*/
|
||||
void set_authored_time (const QDateTime &t);
|
||||
|
||||
/**
|
||||
* @brief Gets the installation date and/or time of the grain
|
||||
*/
|
||||
const QDateTime &installed_time () const
|
||||
{
|
||||
return m_installed_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the installation date and/or time
|
||||
*/
|
||||
void set_installed_time (const QDateTime &t);
|
||||
|
||||
/**
|
||||
* @brief Gets the icon image for the grain.
|
||||
* The preferred image size is 64x64 pixels.
|
||||
* The image may be null image. In this case, a default image is used.
|
||||
*/
|
||||
const QImage &icon () const
|
||||
{
|
||||
return m_icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets icon image
|
||||
*/
|
||||
void set_icon (const QImage &i);
|
||||
|
||||
/**
|
||||
* @brief Gets a screenshot image for documentation.
|
||||
* The image may be null image. In this case, no screenshot is shown.
|
||||
*/
|
||||
const QImage &screenshot () const
|
||||
{
|
||||
return m_screenshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets screenshot image
|
||||
*/
|
||||
void set_screenshot (const QImage &i);
|
||||
|
||||
/**
|
||||
* @brief Gets the absolute file path of the installed grain
|
||||
* This is the file path to the grain folder.
|
||||
*/
|
||||
const std::string &path () const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the absolute file path of the installed grain
|
||||
*/
|
||||
void set_path (const std::string &p);
|
||||
|
||||
/**
|
||||
* @brief Gets the download URL
|
||||
* The download URL is the place from which the grain was installed originally.
|
||||
*/
|
||||
const std::string &url () const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the download URL
|
||||
*/
|
||||
void set_url (const std::string &u);
|
||||
|
||||
/**
|
||||
* @brief Gets the dependencies of the grain
|
||||
* Grains this grain depends on are installed automatically when the grain
|
||||
* is installed.
|
||||
*/
|
||||
const std::vector<Dependency> &dependencies () const
|
||||
{
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the dependencies of the grain (non-const)
|
||||
*/
|
||||
std::vector<Dependency> &dependencies ()
|
||||
{
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dependency iterator (begin)
|
||||
*/
|
||||
std::vector<Dependency>::const_iterator begin_dependencies () const
|
||||
{
|
||||
return m_dependencies.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dependency iterator (end)
|
||||
*/
|
||||
std::vector<Dependency>::const_iterator end_dependencies () const
|
||||
{
|
||||
return m_dependencies.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a dependency
|
||||
*/
|
||||
void add_dependency (const Dependency &dep)
|
||||
{
|
||||
m_dependencies.push_back (dep);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the collection is read-only
|
||||
*/
|
||||
bool is_readonly () const;
|
||||
|
||||
/**
|
||||
* @brief Loads the data from a given file
|
||||
* This method will *not* set the path.
|
||||
*/
|
||||
void load (const std::string &file_path);
|
||||
|
||||
/**
|
||||
* @brief Loads the data from a given stream
|
||||
*/
|
||||
void load (tl::InputStream &stream);
|
||||
|
||||
/**
|
||||
* @brief Saves the data to the path inside the grain folder given by the "path" property
|
||||
*/
|
||||
void save () const;
|
||||
|
||||
/**
|
||||
* @brief Saves the data to the given file
|
||||
*/
|
||||
void save (const std::string &file_path) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the XML structure representing a grain
|
||||
*/
|
||||
static tl::XMLElementList &xml_struct ();
|
||||
|
||||
/**
|
||||
* @brief Compares two version strings
|
||||
* Returns -1 if v1 < v2, 0 if v1 == v2 and 1 if v1 > v2.
|
||||
* Malformed versions are read gracefully. Letters and non-digits are skipped.
|
||||
* Missing numbers are read as 0. Hence "1.0 == 1" for example.
|
||||
*/
|
||||
static int compare_versions (const std::string &v1, const std::string &v2);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the given version string is a valid version
|
||||
*/
|
||||
static bool valid_version (const std::string &v);
|
||||
|
||||
/**
|
||||
* @brief Checks whether the given string is a valid name
|
||||
*/
|
||||
static bool valid_name (const std::string &n);
|
||||
|
||||
/**
|
||||
* @brief Detects a grain from the given directory
|
||||
* This method will return a grain constructed from the given directory.
|
||||
* The data is read from "path/grain.xml". This method will throw an
|
||||
* exception if an error occurs during reading.
|
||||
*/
|
||||
static SaltGrain from_path (const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Loads the grain from the given URL
|
||||
* This method will return a grain constructed from the downloaded data.
|
||||
* The data is read from "URL/grain.xml". This method will throw an
|
||||
* exception if an error occurs during reading.
|
||||
*/
|
||||
static SaltGrain from_url (const std::string &url);
|
||||
|
||||
/**
|
||||
* @brief Forms the spec file download URL from a given download URL
|
||||
*/
|
||||
static std::string spec_url (const std::string &url);
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the given path represents is a grain
|
||||
*/
|
||||
static bool is_grain (const std::string &path);
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_version;
|
||||
std::string m_path;
|
||||
std::string m_url;
|
||||
std::string m_title;
|
||||
std::string m_doc, m_doc_url;
|
||||
std::string m_author;
|
||||
std::string m_author_contact;
|
||||
std::string m_license;
|
||||
QDateTime m_authored_time, m_installed_time;
|
||||
QImage m_icon, m_screenshot;
|
||||
std::vector<Dependency> m_dependencies;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltGrainDetailsTextWidget.h"
|
||||
#include "laySaltGrain.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QBuffer>
|
||||
#include <QIcon>
|
||||
#include <QPainter>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
SaltGrainDetailsTextWidget::SaltGrainDetailsTextWidget (QWidget *w)
|
||||
: QTextBrowser (w), mp_grain (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void SaltGrainDetailsTextWidget::set_grain (SaltGrain *g)
|
||||
{
|
||||
mp_grain = g;
|
||||
setHtml (details_text ());
|
||||
}
|
||||
|
||||
QVariant
|
||||
SaltGrainDetailsTextWidget::loadResource (int type, const QUrl &url)
|
||||
{
|
||||
if (url.path () == QString::fromUtf8 ("/icon")) {
|
||||
|
||||
int icon_dim = 64;
|
||||
|
||||
if (!mp_grain || mp_grain->icon ().isNull ()) {
|
||||
|
||||
return QImage (":/salt_icon.png");
|
||||
|
||||
} else {
|
||||
|
||||
QImage img = mp_grain->icon ();
|
||||
if (img.width () != icon_dim || img.height () != icon_dim) {
|
||||
|
||||
img = img.scaled (QSize (icon_dim, icon_dim), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
QImage final_img (icon_dim, icon_dim, QImage::Format_ARGB32);
|
||||
final_img.fill (QColor (0, 0, 0, 0));
|
||||
QPainter painter (&final_img);
|
||||
painter.drawImage ((icon_dim - img.width ()) / 2, (icon_dim - img.height ()) / 2, img);
|
||||
|
||||
return final_img;
|
||||
|
||||
} else {
|
||||
return img;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (url.path () == QString::fromUtf8 ("/screenshot")) {
|
||||
|
||||
QImage s = mp_grain->screenshot ().convertToFormat (QImage::Format_ARGB32_Premultiplied);
|
||||
|
||||
QImage smask (s.size (), QImage::Format_ARGB32_Premultiplied);
|
||||
smask.fill (QColor (0, 0, 0, 0));
|
||||
{
|
||||
int border = 0;
|
||||
int radius = 6;
|
||||
|
||||
QPainter painter (&smask);
|
||||
|
||||
painter.setRenderHint (QPainter::Antialiasing);
|
||||
|
||||
painter.setCompositionMode (QPainter::CompositionMode_Source);
|
||||
for (int b = border; b > 0; --b) {
|
||||
QPen pen (QColor (255, 255, 255, ((border - b + 1) * 255) / border));
|
||||
pen.setWidth (b * 2 + 1);
|
||||
painter.setBrush (Qt::NoBrush);
|
||||
painter.setPen (pen);
|
||||
painter.drawRoundedRect (QRectF (border, border, s.width () - 2 * border, s.height () - 2 * border), radius, radius, Qt::AbsoluteSize);
|
||||
}
|
||||
|
||||
painter.setPen (Qt::white);
|
||||
painter.setBrush (Qt::white);
|
||||
painter.drawRoundedRect (QRectF (border, border, s.width () - 2 * border, s.height () - 2 * border), radius, radius, Qt::AbsoluteSize);
|
||||
}
|
||||
|
||||
{
|
||||
QPainter painter (&s);
|
||||
painter.setCompositionMode (QPainter::CompositionMode_DestinationIn);
|
||||
painter.drawImage (0, 0, smask);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
} else {
|
||||
return QTextBrowser::loadResource (type, url);
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
SaltGrainDetailsTextWidget::details_text ()
|
||||
{
|
||||
SaltGrain *g = mp_grain;
|
||||
if (! g) {
|
||||
return QString ();
|
||||
}
|
||||
|
||||
QBuffer buffer;
|
||||
buffer.open (QIODevice::WriteOnly);
|
||||
QTextStream stream (&buffer);
|
||||
stream.setCodec ("UTF-8");
|
||||
|
||||
stream << "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/></head><body>";
|
||||
|
||||
stream << "<table cellpadding=\"6\"><tr>";
|
||||
stream << "<td><img src=\":/icon\" width=\"64\" height=\"64\"/></td>";
|
||||
|
||||
stream << "<td>";
|
||||
stream << "<h1>";
|
||||
stream << tl::to_qstring (tl::escaped_to_html (g->name ())) << " " << tl::to_qstring (tl::escaped_to_html (g->version ()));
|
||||
stream << "</h1>";
|
||||
if (! g->title ().empty()) {
|
||||
stream << "<h3>" << tl::to_qstring (tl::escaped_to_html (g->title ())) << "</h3>";
|
||||
}
|
||||
|
||||
if (g->version ().empty ()) {
|
||||
stream << "<p><i><font color='gray'>";
|
||||
stream << QObject::tr ("This package does not have a version. "
|
||||
"Use the <version> element of the specification file or edit the package properties to provide a version.");
|
||||
stream << "</font></i></p>";
|
||||
}
|
||||
|
||||
if (g->title ().empty ()) {
|
||||
stream << "<p><i><font color='gray'>";
|
||||
stream << QObject::tr ("This package does not have a title. "
|
||||
"Use the <title> element of the specification file or edit the package properties to provide a title.");
|
||||
stream << "</font></i></p>";
|
||||
}
|
||||
|
||||
stream << "<p><br/>";
|
||||
if (! g->doc ().empty ()) {
|
||||
stream << tl::to_qstring (tl::escaped_to_html (g->doc ()));
|
||||
} else {
|
||||
stream << "<i><font color='gray'>";
|
||||
stream << QObject::tr ("This package does not have a description. "
|
||||
"Use the <doc> element of the specification file or edit the package properties to provide a description.");
|
||||
stream << "</font></i>";
|
||||
}
|
||||
stream << "</p>";
|
||||
|
||||
stream << "<p>";
|
||||
if (! g->author ().empty ()) {
|
||||
stream << "<b>" << QObject::tr ("Author") << ":</b> " << tl::to_qstring (tl::escaped_to_html (g->author ())) << " ";
|
||||
if (! g->author_contact ().empty ()) {
|
||||
stream << "(" << tl::to_qstring (tl::escaped_to_html (g->author_contact ())) << ")";
|
||||
}
|
||||
if (!g->authored_time ().isNull ()) {
|
||||
stream << "<br/>";
|
||||
stream << "<b>" << QObject::tr ("Released") << ":</b> " << g->authored_time ().date ().toString (Qt::ISODate);
|
||||
}
|
||||
} else {
|
||||
stream << "<i><font color='gray'>";
|
||||
stream << QObject::tr ("This package does not have a author information. "
|
||||
"Use the <author>, <authored-time> and <author-contact> elements of the specification file or edit the package properties to provide authoring information.");
|
||||
stream << "</font></i>";
|
||||
}
|
||||
stream << "</p>";
|
||||
|
||||
stream << "<p>";
|
||||
if (! g->license ().empty ()) {
|
||||
stream << "<b>" << QObject::tr ("License") << ":</b> " << tl::to_qstring (tl::escaped_to_html (g->license ())) << " ";
|
||||
} else {
|
||||
stream << "<i><font color='gray'>";
|
||||
stream << QObject::tr ("This package does not have license information. "
|
||||
"Use the <license> elements of the specification file or edit the package properties to provide license information.");
|
||||
stream << "</font></i>";
|
||||
}
|
||||
stream << "</p>";
|
||||
|
||||
stream << "<p>";
|
||||
if (! g->doc_url ().empty ()) {
|
||||
stream << "<b>" << QObject::tr ("Documentation link") << ":</b> <a href=\"" << tl::to_qstring (g->doc_url ()) << "\">" << tl::to_qstring (tl::escaped_to_html (g->doc_url ())) << "</a>";
|
||||
} else {
|
||||
stream << "<i><font color='gray'>";
|
||||
stream << QObject::tr ("This package does not have a documentation link. "
|
||||
"Use the <doc-url> element of the specification file or edit the package properties to provide a link.");
|
||||
stream << "</font></i>";
|
||||
}
|
||||
stream << "</p>";
|
||||
|
||||
if (! g->screenshot ().isNull ()) {
|
||||
stream << "<br/>";
|
||||
stream << "<h3>" << QObject::tr ("Screenshot") << "</h3><p><img src=\":/screenshot\"/></p>";
|
||||
}
|
||||
|
||||
stream << "<br/>";
|
||||
stream << "<h3>" << QObject::tr ("Installation") << "</h3>";
|
||||
|
||||
stream << "<p><b>" << QObject::tr ("Installation path: ") << "</b>" << tl::to_qstring (tl::escaped_to_html (g->path ())) << "</p>";
|
||||
if (! g->url ().empty ()) {
|
||||
stream << "<p><b>" << QObject::tr ("Download URL: ") << "</b>" << tl::to_qstring (tl::escaped_to_html (g->url ())) << "</p>";
|
||||
}
|
||||
if (! g->installed_time ().isNull ()) {
|
||||
stream << "<p><b>" << QObject::tr ("Installed: ") << "</b>" << g->installed_time ().toString () << "</p>";
|
||||
}
|
||||
if (! g->dependencies ().empty ()) {
|
||||
stream << "<p><b>" << QObject::tr ("Depends on: ") << "</b><br/>";
|
||||
for (std::vector<lay::SaltGrain::Dependency>::const_iterator d = g->dependencies ().begin (); d != g->dependencies ().end (); ++d) {
|
||||
stream << " " << tl::to_qstring (tl::escaped_to_html (d->name)) << " ";
|
||||
stream << tl::to_qstring (tl::escaped_to_html (d->version));
|
||||
if (! d->url.empty ()) {
|
||||
stream << " - ";
|
||||
stream << "[" << tl::to_qstring (tl::escaped_to_html (d->url)) << "]<br/>";
|
||||
}
|
||||
}
|
||||
stream << "</p>";
|
||||
}
|
||||
|
||||
stream << "</td></tr></table>";
|
||||
|
||||
stream << "</body></html>";
|
||||
|
||||
stream.flush ();
|
||||
|
||||
return QString::fromUtf8 (buffer.buffer());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySaltGrainDetailsTextWidget
|
||||
#define HDR_laySaltGrainDetailsTextWidget
|
||||
|
||||
#include <QTextBrowser>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class SaltGrain;
|
||||
|
||||
/**
|
||||
* @brief A specialisation of QTextBrowser that displays the details of the salt grain
|
||||
*/
|
||||
class SaltGrainDetailsTextWidget
|
||||
: public QTextBrowser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SaltGrainDetailsTextWidget (QWidget *w);
|
||||
|
||||
/**
|
||||
* @brief Sets the grain whose details are to be shown
|
||||
*/
|
||||
void set_grain (SaltGrain *g);
|
||||
|
||||
protected:
|
||||
virtual QVariant loadResource (int type, const QUrl &url);
|
||||
|
||||
private:
|
||||
lay::SaltGrain *mp_grain;
|
||||
|
||||
QString details_text ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,632 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltGrainPropertiesDialog.h"
|
||||
#include "laySalt.h"
|
||||
#include "tlString.h"
|
||||
#include "tlExceptions.h"
|
||||
#include "tlHttpStream.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QItemDelegate>
|
||||
#include <QPainter>
|
||||
#include <QCompleter>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief A delegate for editing a field of the dependency list
|
||||
*/
|
||||
class SaltGrainEditDelegate
|
||||
: public QItemDelegate
|
||||
{
|
||||
public:
|
||||
SaltGrainEditDelegate (QWidget *parent)
|
||||
: QItemDelegate (parent)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const
|
||||
{
|
||||
QLineEdit *editor = new QLineEdit (parent);
|
||||
editor->setFrame (false);
|
||||
editor->setTextMargins (2, 0, 2, 0);
|
||||
return editor;
|
||||
}
|
||||
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & /*index*/) const
|
||||
{
|
||||
editor->setGeometry(option.rect);
|
||||
}
|
||||
|
||||
void setEditorData (QWidget *widget, const QModelIndex &index) const
|
||||
{
|
||||
QLineEdit *editor = dynamic_cast<QLineEdit *> (widget);
|
||||
if (editor) {
|
||||
editor->setText (index.model ()->data (index, Qt::UserRole).toString ());
|
||||
}
|
||||
}
|
||||
|
||||
void setModelData (QWidget *widget, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
QLineEdit *editor = dynamic_cast<QLineEdit *> (widget);
|
||||
if (editor) {
|
||||
model->setData (index, QVariant (editor->text ()), Qt::UserRole);
|
||||
}
|
||||
}
|
||||
|
||||
QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex & /*index*/) const
|
||||
{
|
||||
QSize sz = option.fontMetrics.size (Qt::TextSingleLine, QString::fromUtf8 ("M"));
|
||||
sz += QSize (0, 8);
|
||||
return sz;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A delegate for editing a field of the dependency list
|
||||
*/
|
||||
class SaltGrainNameEditDelegate
|
||||
: public SaltGrainEditDelegate
|
||||
{
|
||||
public:
|
||||
SaltGrainNameEditDelegate (QWidget *parent, Salt *salt)
|
||||
: SaltGrainEditDelegate (parent), mp_completer (0)
|
||||
{
|
||||
QStringList names;
|
||||
for (lay::Salt::flat_iterator i = salt->begin_flat (); i != salt->end_flat (); ++i) {
|
||||
names << tl::to_qstring ((*i)->name ());
|
||||
}
|
||||
mp_completer = new QCompleter (names, this);
|
||||
}
|
||||
|
||||
QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QWidget *editor = SaltGrainEditDelegate::createEditor (parent, option, index);
|
||||
QLineEdit *line_edit = dynamic_cast<QLineEdit *> (editor);
|
||||
if (line_edit) {
|
||||
line_edit->setCompleter (mp_completer);
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
public:
|
||||
QCompleter *mp_completer;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// SaltGrainPropertiesDialog implementation
|
||||
|
||||
SaltGrainPropertiesDialog::SaltGrainPropertiesDialog (QWidget *parent)
|
||||
: QDialog (parent), mp_salt (0), m_update_enabled (true)
|
||||
{
|
||||
Ui::SaltGrainPropertiesDialog::setupUi (this);
|
||||
|
||||
m_title = windowTitle ();
|
||||
m_open_label = open_label->text ();
|
||||
|
||||
connect (icon_delete_button, SIGNAL (clicked ()), this, SLOT (reset_icon ()));
|
||||
connect (icon_config_button, SIGNAL (clicked ()), this, SLOT (set_icon ()));
|
||||
connect (screenshot_delete_button, SIGNAL (clicked ()), this, SLOT (reset_screenshot ()));
|
||||
connect (screenshot_config_button, SIGNAL (clicked ()), this, SLOT (set_screenshot ()));
|
||||
connect (doc_url, SIGNAL (textChanged (const QString &)), this, SLOT (url_changed (const QString &)));
|
||||
connect (add_dependency, SIGNAL (clicked ()), this, SLOT (add_dependency_clicked ()));
|
||||
connect (remove_dependency, SIGNAL (clicked ()), this, SLOT (remove_dependency_clicked ()));
|
||||
connect (dependencies, SIGNAL (itemChanged (QTreeWidgetItem *, int)), this, SLOT (dependency_changed (QTreeWidgetItem *, int)));
|
||||
|
||||
dependencies->setItemDelegateForColumn (1, new SaltGrainEditDelegate (dependencies));
|
||||
dependencies->setItemDelegateForColumn (2, new SaltGrainEditDelegate (dependencies));
|
||||
|
||||
url_changed (QString ());
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::update_controls ()
|
||||
{
|
||||
setWindowTitle (m_title + tl::to_qstring (" - " + m_grain.name ()));
|
||||
license_alert->clear ();
|
||||
version_alert->clear ();
|
||||
doc_url_alert->clear ();
|
||||
dependencies_alert->clear ();
|
||||
|
||||
version->setText (tl::to_qstring (m_grain.version ()));
|
||||
title->setText (tl::to_qstring (m_grain.title ()));
|
||||
author->setText (tl::to_qstring (m_grain.author ()));
|
||||
author_contact->setText (tl::to_qstring (m_grain.author_contact ()));
|
||||
doc->setText (tl::to_qstring (m_grain.doc ()));
|
||||
doc_url->setText (tl::to_qstring (m_grain.doc_url ()));
|
||||
license->setText (tl::to_qstring (m_grain.license ()));
|
||||
|
||||
dependencies->clear ();
|
||||
for (std::vector<SaltGrain::Dependency>::const_iterator d = m_grain.dependencies ().begin (); d != m_grain.dependencies ().end (); ++d) {
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem (dependencies);
|
||||
item->setFlags (item->flags () | Qt::ItemIsEditable);
|
||||
|
||||
item->setData (0, Qt::UserRole, tl::to_qstring (d->name));
|
||||
dependency_changed (item, 0);
|
||||
item->setData (1, Qt::UserRole, tl::to_qstring (d->version));
|
||||
dependency_changed (item, 1);
|
||||
item->setData (2, Qt::UserRole, tl::to_qstring (d->url));
|
||||
dependency_changed (item, 2);
|
||||
|
||||
dependencies->addTopLevelItem (item);
|
||||
|
||||
}
|
||||
|
||||
update_icon ();
|
||||
update_screenshot ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::update_icon ()
|
||||
{
|
||||
if (m_grain.icon ().isNull ()) {
|
||||
icon_config_button->setIcon (QIcon (":/salt_icon.png"));
|
||||
} else {
|
||||
QImage img = m_grain.icon ();
|
||||
if (img.width () == icon_config_button->iconSize ().width ()) {
|
||||
icon_config_button->setIcon (QIcon (QPixmap::fromImage (img)));
|
||||
} else {
|
||||
icon_config_button->setIcon (QIcon (QPixmap::fromImage (img.scaled (icon_config_button->iconSize (), Qt::KeepAspectRatio, Qt::SmoothTransformation))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::update_screenshot ()
|
||||
{
|
||||
if (m_grain.screenshot ().isNull ()) {
|
||||
screenshot_config_button->setIcon (QIcon (":/add.png"));
|
||||
} else {
|
||||
QImage img = m_grain.screenshot ();
|
||||
if (img.width () == screenshot_config_button->iconSize ().width ()) {
|
||||
screenshot_config_button->setIcon (QIcon (QPixmap::fromImage (img)));
|
||||
} else {
|
||||
screenshot_config_button->setIcon (QIcon (QPixmap::fromImage (img.scaled (screenshot_config_button->iconSize (), Qt::KeepAspectRatio, Qt::SmoothTransformation))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::update_data ()
|
||||
{
|
||||
m_grain.set_version (tl::to_string (version->text ()));
|
||||
m_grain.set_title (tl::to_string (title->text ()));
|
||||
m_grain.set_author (tl::to_string (author->text ()));
|
||||
m_grain.set_author_contact (tl::to_string (author_contact->text ()));
|
||||
m_grain.set_doc (tl::to_string (doc->toPlainText ()));
|
||||
m_grain.set_doc_url (tl::to_string (doc_url->text ()));
|
||||
m_grain.set_license (tl::to_string (license->text ()));
|
||||
|
||||
m_grain.dependencies ().clear ();
|
||||
for (int i = 0; i < dependencies->topLevelItemCount (); ++i) {
|
||||
|
||||
QTreeWidgetItem *item = dependencies->topLevelItem (i);
|
||||
QString name = item->data (0, Qt::UserRole).toString ().simplified ();
|
||||
QString version = item->data (1, Qt::UserRole).toString ().simplified ();
|
||||
QString url = item->data (2, Qt::UserRole).toString ().simplified ();
|
||||
|
||||
if (! name.isEmpty ()) {
|
||||
lay::SaltGrain::Dependency dep = lay::SaltGrain::Dependency ();
|
||||
dep.name = tl::to_string (name);
|
||||
dep.version = tl::to_string (version);
|
||||
dep.url = tl::to_string (url);
|
||||
m_grain.dependencies ().push_back (dep);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::dependency_changed (QTreeWidgetItem *item, int column)
|
||||
{
|
||||
if (! m_update_enabled) {
|
||||
return;
|
||||
}
|
||||
m_update_enabled = false;
|
||||
|
||||
std::string name = tl::to_string (item->data (0, Qt::UserRole).toString ().simplified ());
|
||||
SaltGrain *g = mp_salt->grain_by_name (name);
|
||||
|
||||
if (column == 0 && mp_salt) {
|
||||
|
||||
item->setData (0, Qt::EditRole, tl::to_qstring (name));
|
||||
|
||||
// set URL and version for known grains
|
||||
if (name == m_grain.name ()) {
|
||||
|
||||
item->setData (1, Qt::UserRole, QString ());
|
||||
item->setData (2, Qt::UserRole, QString ());
|
||||
// placeholder texts:
|
||||
item->setData (1, Qt::EditRole, QString ());
|
||||
item->setData (2, Qt::EditRole, tr ("(must not depend on itself)"));
|
||||
|
||||
} else {
|
||||
|
||||
if (g) {
|
||||
item->setData (1, Qt::UserRole, tl::to_qstring (g->version ()));
|
||||
item->setData (2, Qt::UserRole, QString ());
|
||||
// placeholder texts:
|
||||
item->setData (1, Qt::EditRole, tl::to_qstring (g->version ()));
|
||||
if (! g->url ().empty ()) {
|
||||
item->setData (2, Qt::EditRole, tl::to_qstring ("(" + g->url () + ")"));
|
||||
} else {
|
||||
item->setData (2, Qt::EditRole, tr ("(from repository)"));
|
||||
}
|
||||
} else {
|
||||
item->setData (1, Qt::UserRole, QString ());
|
||||
item->setData (2, Qt::UserRole, QString ());
|
||||
// placeholder texts:
|
||||
item->setData (1, Qt::EditRole, QString ());
|
||||
item->setData (2, Qt::EditRole, tr ("(from repository)"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (column == 1) {
|
||||
|
||||
QString text = item->data (column, Qt::UserRole).toString ();
|
||||
if (! text.isEmpty ()) {
|
||||
item->setData (1, Qt::EditRole, text);
|
||||
} else if (g) {
|
||||
item->setData (1, Qt::EditRole, tl::to_qstring (g->version ()));
|
||||
}
|
||||
|
||||
} else if (column == 2) {
|
||||
|
||||
QString text = item->data (column, Qt::UserRole).toString ();
|
||||
if (! text.isEmpty ()) {
|
||||
item->setData (2, Qt::EditRole, text);
|
||||
} else if (g) {
|
||||
if (! g->url ().empty ()) {
|
||||
item->setData (2, Qt::EditRole, tl::to_qstring ("(" + g->url () + ")"));
|
||||
} else {
|
||||
item->setData (2, Qt::EditRole, tr ("(from repository)"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_update_enabled = true;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::url_changed (const QString &url)
|
||||
{
|
||||
// inserts the URL into the label
|
||||
open_label->setText (m_open_label.arg (url));
|
||||
open_label->setEnabled (! url.isEmpty ());
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::set_icon ()
|
||||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
const int max_dim = 256;
|
||||
|
||||
QString fileName = QFileDialog::getOpenFileName (this, tr ("Pick Icon Image File"), m_image_dir, tr ("Images (*.png *.jpg);;All Files (*)"));
|
||||
if (! fileName.isNull ()) {
|
||||
|
||||
bool ok = true;
|
||||
QImage img = QImage (fileName);
|
||||
if (img.width () > max_dim || img.height () > max_dim) {
|
||||
if (QMessageBox::warning (this, tr ("Image Too Big"),
|
||||
tr ("Icon image too big - must be %1x%2 pixels max, but is %3x%4.\n\nScale image?").arg (max_dim).arg (max_dim).arg (img.width ()).arg (img.height ()),
|
||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
|
||||
ok = false;
|
||||
} else {
|
||||
img = img.scaled (max_dim, max_dim, Qt::KeepAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
m_grain.set_icon (img);
|
||||
m_image_dir = QFileInfo (fileName).path ();
|
||||
update_icon ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::reset_icon ()
|
||||
{
|
||||
m_grain.set_icon (QImage ());
|
||||
update_icon ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::set_screenshot ()
|
||||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
const int max_dim = 1024;
|
||||
|
||||
QString fileName = QFileDialog::getOpenFileName (this, tr ("Pick Showcase Image File"), m_image_dir, tr ("Images (*.png *.jpg);;All Files (*)"));
|
||||
if (! fileName.isNull ()) {
|
||||
|
||||
bool ok = true;
|
||||
QImage img = QImage (fileName);
|
||||
if (img.width () > max_dim || img.height () > max_dim) {
|
||||
if (QMessageBox::warning (this, tr ("Image Too Big"),
|
||||
tr ("Showcase image too big - must be %1x%2 pixels max, but is %3x%4.\n\nScale image?").arg (max_dim).arg (max_dim).arg (img.width ()).arg (img.height ()),
|
||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
|
||||
ok = false;
|
||||
} else {
|
||||
img = img.scaled (max_dim, max_dim, Qt::KeepAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
m_grain.set_screenshot (img);
|
||||
m_image_dir = QFileInfo (fileName).path ();
|
||||
update_screenshot ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::reset_screenshot ()
|
||||
{
|
||||
m_grain.set_screenshot (QImage ());
|
||||
update_screenshot ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::add_dependency_clicked ()
|
||||
{
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem (dependencies);
|
||||
item->setFlags (item->flags () | Qt::ItemIsEditable);
|
||||
dependencies->addTopLevelItem (item);
|
||||
dependencies->setCurrentItem (dependencies->topLevelItem (dependencies->topLevelItemCount () - 1));
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::remove_dependency_clicked ()
|
||||
{
|
||||
int index = dependencies->indexOfTopLevelItem (dependencies->currentItem ());
|
||||
if (index >= 0 && index < dependencies->topLevelItemCount ()) {
|
||||
delete dependencies->topLevelItem (index);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class DependencyGraph
|
||||
{
|
||||
public:
|
||||
DependencyGraph (Salt *salt)
|
||||
{
|
||||
for (lay::Salt::flat_iterator i = salt->begin_flat (); i != salt->end_flat (); ++i) {
|
||||
m_name_to_grain.insert (std::make_pair ((*i)->name (), *i));
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid_name (const std::string &name) const
|
||||
{
|
||||
return m_name_to_grain.find (name) != m_name_to_grain.end ();
|
||||
}
|
||||
|
||||
const lay::SaltGrain *grain_for_name (const std::string &name) const
|
||||
{
|
||||
std::map <std::string, const lay::SaltGrain *>::const_iterator n = m_name_to_grain.find (name);
|
||||
if (n != m_name_to_grain.end ()) {
|
||||
return n->second;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void check_circular (const lay::SaltGrain *current, const lay::SaltGrain *new_dep)
|
||||
{
|
||||
std::vector <const lay::SaltGrain *> path;
|
||||
path.push_back (current);
|
||||
check_circular_follow (new_dep, path);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map <std::string, const lay::SaltGrain *> m_name_to_grain;
|
||||
|
||||
void check_circular_follow (const lay::SaltGrain *current, std::vector <const lay::SaltGrain *> &path)
|
||||
{
|
||||
if (! current) {
|
||||
return;
|
||||
}
|
||||
|
||||
path.push_back (current);
|
||||
|
||||
for (std::vector <const lay::SaltGrain *>::const_iterator p = path.begin (); p != path.end () - 1; ++p) {
|
||||
if (*p == current) {
|
||||
circular_reference_error (path);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<SaltGrain::Dependency>::const_iterator d = current->dependencies ().begin (); d != current->dependencies ().end (); ++d) {
|
||||
check_circular_follow (grain_for_name (d->name), path);
|
||||
}
|
||||
|
||||
path.pop_back ();
|
||||
}
|
||||
|
||||
void circular_reference_error (std::vector <const lay::SaltGrain *> &path)
|
||||
{
|
||||
std::string msg = tl::to_string (QObject::tr ("The following path forms a circular dependency: "));
|
||||
for (std::vector <const lay::SaltGrain *>::const_iterator p = path.begin (); p != path.end (); ++p) {
|
||||
if (p != path.begin ()) {
|
||||
msg += "->";
|
||||
}
|
||||
msg += (*p)->name ();
|
||||
}
|
||||
throw tl::Exception (msg);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrainPropertiesDialog::accept ()
|
||||
{
|
||||
update_data ();
|
||||
|
||||
// Perform some checks
|
||||
|
||||
// license
|
||||
license_alert->clear ();
|
||||
if (m_grain.license ().empty ()) {
|
||||
license_alert->warn () << tr ("License field is empty. Please consider specifying a license model.") << tl::endl
|
||||
<< tr ("A license model tells users whether and how to use the source code of the package.");
|
||||
}
|
||||
|
||||
// version
|
||||
version_alert->clear ();
|
||||
if (m_grain.version ().empty ()) {
|
||||
version_alert->warn () << tr ("Version field is empty. Please consider specifying a version number.") << tl::endl
|
||||
<< tr ("Versions help the system to apply upgrades if required.");
|
||||
} else if (! SaltGrain::valid_version (m_grain.version ())) {
|
||||
version_alert->error () << tr ("'%1' is not a valid version string. A version string needs to be numeric (like '1.2.3' or '4.5'').").arg (tl::to_qstring (m_grain.version ()));
|
||||
}
|
||||
|
||||
// doc URL
|
||||
doc_url_alert->clear ();
|
||||
if (! m_grain.doc_url ().empty ()) {
|
||||
tl::InputHttpStream stream (m_grain.doc_url ());
|
||||
try {
|
||||
char b;
|
||||
stream.read (&b, 1);
|
||||
} catch (tl::Exception &ex) {
|
||||
doc_url_alert->error () << tr ("Attempt to read documentation URL failed. Error details follow.") << tl::endl
|
||||
<< tr ("URL: ") << m_grain.doc_url () << tl::endl
|
||||
<< tr ("Message: ") << ex.msg ();
|
||||
}
|
||||
}
|
||||
|
||||
// dependencies
|
||||
dependencies_alert->clear ();
|
||||
DependencyGraph dep (mp_salt);
|
||||
std::set <std::string> dep_seen;
|
||||
for (std::vector<SaltGrain::Dependency>::const_iterator d = m_grain.dependencies ().begin (); d != m_grain.dependencies ().end (); ++d) {
|
||||
|
||||
if (! SaltGrain::valid_name (d->name)) {
|
||||
dependencies_alert->error () << tr ("'%1' is not a valid package name").arg (tl::to_qstring (d->name)) << tl::endl
|
||||
<< tr ("Valid package names are words (letters, digits, underscores).") << tl::endl
|
||||
<< tr ("Package groups can be specified in the form 'group/package'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dep_seen.find (d->name) != dep_seen.end ()) {
|
||||
dependencies_alert->error () << tr ("Duplicate dependency '%1'").arg (tl::to_qstring (d->name)) << tl::endl
|
||||
<< tr ("A package cannot be dependent on the same package twice. Remove on entry.");
|
||||
continue;
|
||||
}
|
||||
dep_seen.insert (d->name);
|
||||
|
||||
if (dep.is_valid_name (d->name)) {
|
||||
try {
|
||||
dep.check_circular (dep.grain_for_name (m_grain.name ()), dep.grain_for_name (d->name));
|
||||
} catch (tl::Exception &ex) {
|
||||
dependencies_alert->error () << ex.msg () << tl::endl
|
||||
<< tr ("Circular dependency means, a package is eventually depending on itself.");
|
||||
}
|
||||
}
|
||||
|
||||
if (d->version.empty ()) {
|
||||
dependencies_alert->warn () << tr ("No version specified for dependency '%1'").arg (tl::to_qstring (d->name)) << tl::endl
|
||||
<< tr ("Please consider giving a version here. Versions help deciding whether a package needs to be updated.") << tl::endl
|
||||
<< tr ("If the dependency package has a version itself, the version is automatically set to it's current version.");
|
||||
}
|
||||
|
||||
if (!d->url.empty ()) {
|
||||
SaltGrain gdep;
|
||||
try {
|
||||
gdep = SaltGrain::from_url (d->url);
|
||||
if (gdep.name () != d->name) {
|
||||
dependencies_alert->error () << tr ("Package name obtained from download URL is not the expected name.") << tl::endl
|
||||
<< tr ("Downloaded name: ") << gdep.name () << tl::endl
|
||||
<< tr ("Expected name: ") << d->name;
|
||||
}
|
||||
} catch (tl::Exception &ex) {
|
||||
dependencies_alert->error () << tr ("Attempt to test-download package from URL failed. Error details follow.") << tl::endl
|
||||
<< tr ("URL: ") << d->url << tl::endl
|
||||
<< tr ("Message: ") << ex.msg ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!license_alert->needs_attention () &&
|
||||
!doc_url_alert->needs_attention () &&
|
||||
!dependencies_alert->needs_attention () &&
|
||||
!version_alert->needs_attention ()) {
|
||||
QDialog::accept ();
|
||||
} else {
|
||||
if (QMessageBox::warning (this, tr ("Issues Encountered"),
|
||||
tr ("Some issues have been found when inspecting the package details.\nThe respective fields are marked with warning icons.\n\nIgnore these issues and commit the package details?"),
|
||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
QDialog::accept ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrainPropertiesDialog::exec_dialog (lay::SaltGrain *grain, lay::Salt *salt)
|
||||
{
|
||||
m_grain = *grain;
|
||||
mp_salt = salt;
|
||||
|
||||
dependencies->setItemDelegateForColumn (0, new SaltGrainNameEditDelegate (dependencies, mp_salt));
|
||||
|
||||
update_controls ();
|
||||
|
||||
bool res = exec ();
|
||||
if (res && *grain != m_grain) {
|
||||
*grain = m_grain;
|
||||
// save modified grain
|
||||
grain->save ();
|
||||
}
|
||||
|
||||
delete dependencies->itemDelegateForColumn (0);
|
||||
dependencies->setItemDelegateForColumn (0, 0);
|
||||
|
||||
mp_salt = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySaltGrainPropertiesDialog
|
||||
#define HDR_laySaltGrainPropertiesDialog
|
||||
|
||||
#include "laySaltGrain.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "ui_SaltGrainPropertiesDialog.h"
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class Salt;
|
||||
|
||||
/**
|
||||
* @brief The dialog for managing the Salt ("Packages")
|
||||
*/
|
||||
class SaltGrainPropertiesDialog
|
||||
: public QDialog, private Ui::SaltGrainPropertiesDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SaltGrainPropertiesDialog (QWidget *parent);
|
||||
|
||||
/**
|
||||
* @brief Executes the dialog for the given grain
|
||||
* If the dialog is committed with "Ok", the new data is written into
|
||||
* the grain provided and "true" is returned. Otherwise, "false" is
|
||||
* returned and the object remains unchanged.
|
||||
*/
|
||||
bool exec_dialog (lay::SaltGrain *grain, lay::Salt *salt);
|
||||
|
||||
/**
|
||||
* @brief Gets the current package index
|
||||
*/
|
||||
lay::Salt *salt ()
|
||||
{
|
||||
return mp_salt;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void reset_icon ();
|
||||
void set_icon ();
|
||||
void reset_screenshot ();
|
||||
void set_screenshot ();
|
||||
void url_changed (const QString &url);
|
||||
void add_dependency_clicked ();
|
||||
void remove_dependency_clicked ();
|
||||
void dependency_changed (QTreeWidgetItem *item, int column);
|
||||
|
||||
protected:
|
||||
void accept ();
|
||||
|
||||
private:
|
||||
lay::SaltGrain m_grain;
|
||||
lay::Salt *mp_salt;
|
||||
QString m_title;
|
||||
QString m_open_label;
|
||||
QString m_image_dir;
|
||||
bool m_update_enabled;
|
||||
|
||||
void update_controls ();
|
||||
void update_data ();
|
||||
void update_icon ();
|
||||
void update_screenshot ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltGrains.h"
|
||||
#include "tlString.h"
|
||||
#include "tlFileUtils.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QResource>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
SaltGrains::SaltGrains ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrains::operator== (const SaltGrains &other) const
|
||||
{
|
||||
return m_name == other.m_name &&
|
||||
m_path == other.m_path &&
|
||||
m_title == other.m_title &&
|
||||
m_collections == other.m_collections &&
|
||||
m_grains == other.m_grains;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrains::set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrains::set_title (const std::string &t)
|
||||
{
|
||||
m_title = t;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrains::set_path (const std::string &p)
|
||||
{
|
||||
m_path = p;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrains::add_collection (const SaltGrains &collection)
|
||||
{
|
||||
m_collections.push_back (collection);
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrains::remove_collection (collection_iterator iter, bool with_files)
|
||||
{
|
||||
// NOTE: this is kind of inefficient, but in order to maintain the const iterator semantics this approach is required
|
||||
for (collections_type::iterator i = m_collections.begin (); i != m_collections.end (); ++i) {
|
||||
if (i == iter) {
|
||||
if (with_files && !tl::rm_dir_recursive (tl::to_qstring (i->path ()))) {
|
||||
return false;
|
||||
}
|
||||
m_collections.erase (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrains::add_grain (const SaltGrain &grain)
|
||||
{
|
||||
m_grains.push_back (grain);
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrains::remove_grain (grain_iterator iter, bool with_files)
|
||||
{
|
||||
// NOTE: this is kind of inefficient, but in order to maintain the const iterator semantics this approach is required
|
||||
for (grains_type::iterator i = m_grains.begin (); i != m_grains.end (); ++i) {
|
||||
if (i == iter) {
|
||||
if (with_files && !tl::rm_dir_recursive (tl::to_qstring (i->path ()))) {
|
||||
return false;
|
||||
}
|
||||
m_grains.erase (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrains::is_empty () const
|
||||
{
|
||||
if (! m_grains.empty ()) {
|
||||
return false;
|
||||
}
|
||||
for (collections_type::const_iterator i = m_collections.begin (); i != m_collections.end (); ++i) {
|
||||
if (!i->is_empty ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SaltGrains::is_readonly () const
|
||||
{
|
||||
return QFileInfo (tl::to_qstring (path ())).isWritable ();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A helper class required because directory traversal is not supported by QResource directly
|
||||
*/
|
||||
class OpenResource
|
||||
: public QResource
|
||||
{
|
||||
public:
|
||||
using QResource::isDir;
|
||||
using QResource::isFile;
|
||||
using QResource::children;
|
||||
|
||||
OpenResource (const QString &path)
|
||||
: QResource (path)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
SaltGrains
|
||||
SaltGrains::from_path (const std::string &path, const std::string &prefix)
|
||||
{
|
||||
tl_assert (! path.empty ());
|
||||
|
||||
SaltGrains grains;
|
||||
grains.set_path (path);
|
||||
|
||||
if (path[0] != ':') {
|
||||
|
||||
QDir dir (tl::to_qstring (path));
|
||||
QStringList entries = dir.entryList (QDir::NoDotAndDotDot | QDir::Dirs, QDir::Name);
|
||||
for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) {
|
||||
|
||||
std::string new_prefix = prefix;
|
||||
if (! new_prefix.empty ()) {
|
||||
new_prefix += "/";
|
||||
}
|
||||
new_prefix += tl::to_string (*e);
|
||||
|
||||
std::string epath = tl::to_string (dir.absoluteFilePath (*e));
|
||||
if (SaltGrain::is_grain (epath)) {
|
||||
try {
|
||||
SaltGrain g (SaltGrain::from_path (epath));
|
||||
g.set_name (new_prefix);
|
||||
grains.add_grain (g);
|
||||
} catch (...) {
|
||||
// ignore errors (TODO: what to do here?)
|
||||
}
|
||||
} else if (QFileInfo (tl::to_qstring (epath)).isDir ()) {
|
||||
SaltGrains c = SaltGrains::from_path (epath, new_prefix);
|
||||
c.set_name (new_prefix);
|
||||
if (! c.is_empty ()) {
|
||||
grains.add_collection (c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
OpenResource resource (tl::to_qstring (path));
|
||||
if (resource.isDir ()) {
|
||||
|
||||
QStringList templ_dir = resource.children ();
|
||||
for (QStringList::const_iterator t = templ_dir.begin (); t != templ_dir.end (); ++t) {
|
||||
|
||||
std::string new_prefix = prefix;
|
||||
if (! new_prefix.empty ()) {
|
||||
new_prefix += "/";
|
||||
}
|
||||
new_prefix += tl::to_string (*t);
|
||||
|
||||
std::string epath = path + "/" + tl::to_string (*t);
|
||||
|
||||
if (SaltGrain::is_grain (epath)) {
|
||||
try {
|
||||
SaltGrain g (SaltGrain::from_path (epath));
|
||||
g.set_name (new_prefix);
|
||||
grains.add_grain (g);
|
||||
} catch (...) {
|
||||
// ignore errors (TODO: what to do here?)
|
||||
}
|
||||
} else if (OpenResource (tl::to_qstring (epath)).isDir ()) {
|
||||
SaltGrains c = SaltGrains::from_path (epath, new_prefix);
|
||||
c.set_name (new_prefix);
|
||||
if (! c.is_empty ()) {
|
||||
grains.add_collection (c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return grains;
|
||||
}
|
||||
|
||||
static tl::XMLElementList s_group_struct =
|
||||
tl::make_member (&SaltGrains::name, &SaltGrains::set_name, "name") +
|
||||
tl::make_element (&SaltGrains::begin_collections, &SaltGrains::end_collections, &SaltGrains::add_collection, "group", &s_group_struct) +
|
||||
tl::make_element (&SaltGrains::begin_grains, &SaltGrains::end_grains, &SaltGrains::add_grain, "salt-grain", SaltGrain::xml_struct ());
|
||||
|
||||
static tl::XMLStruct<lay::SaltGrains> s_xml_struct ("salt-mine", s_group_struct);
|
||||
|
||||
void
|
||||
SaltGrains::load (const std::string &p)
|
||||
{
|
||||
tl::XMLFileSource source (p);
|
||||
s_xml_struct.parse (source, *this);
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrains::load (tl::InputStream &p)
|
||||
{
|
||||
tl::XMLStreamSource source (p);
|
||||
s_xml_struct.parse (source, *this);
|
||||
}
|
||||
|
||||
void
|
||||
SaltGrains::save (const std::string &p) const
|
||||
{
|
||||
tl::OutputStream os (p, tl::OutputStream::OM_Plain);
|
||||
s_xml_struct.write (os, *this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySaltGrains
|
||||
#define HDR_laySaltGrains
|
||||
|
||||
#include "layCommon.h"
|
||||
#include "laySaltGrain.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A class representing a collection of grains (packages)
|
||||
* A collection can have child collections and grains (leafs).
|
||||
*/
|
||||
class LAY_PUBLIC SaltGrains
|
||||
{
|
||||
public:
|
||||
typedef std::list<SaltGrains> collections_type;
|
||||
typedef collections_type::const_iterator collection_iterator;
|
||||
typedef std::list<SaltGrain> grains_type;
|
||||
typedef grains_type::const_iterator grain_iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor: creates an empty collection
|
||||
*/
|
||||
SaltGrains ();
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const SaltGrains &other) const;
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
*/
|
||||
bool operator!= (const SaltGrains &other) const
|
||||
{
|
||||
return !operator== (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the grain collection
|
||||
*
|
||||
* The name is either a plain name (a word) or a path into a collection.
|
||||
* Name paths are formed using the "/" separator. "mycollection" is a plain name,
|
||||
* while "mycollection/subcollection" is a collection within a collection.
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the name of the grain collection
|
||||
*/
|
||||
void set_name (const std::string &p);
|
||||
|
||||
/**
|
||||
* @brief Gets the title of the grain collection
|
||||
*
|
||||
* The title is a brief description that is shown in the title of the
|
||||
* package manager.
|
||||
*/
|
||||
const std::string &title () const
|
||||
{
|
||||
return m_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the title of the grain collection
|
||||
*/
|
||||
void set_title (const std::string &t);
|
||||
|
||||
/**
|
||||
* @brief Gets the absolute file path of the installed grain
|
||||
* This is the file path to the grain folder.
|
||||
*/
|
||||
const std::string &path () const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the absolute file path of the installed grain
|
||||
*/
|
||||
void set_path (const std::string &p);
|
||||
|
||||
/**
|
||||
* @brief Gets the collections which are members of this collection (begin iterator)
|
||||
*/
|
||||
collection_iterator begin_collections () const
|
||||
{
|
||||
return m_collections.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the collections which are members of this collection (end iterator)
|
||||
*/
|
||||
collection_iterator end_collections () const
|
||||
{
|
||||
return m_collections.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a collection to this collection
|
||||
*/
|
||||
void add_collection (const SaltGrains &collection);
|
||||
|
||||
/**
|
||||
* @brief Removes the collection given by the collection iterator
|
||||
* If "with_files" is true, also the folder and all sub-folders will be removed
|
||||
* @return true, if the remove was successful.
|
||||
*/
|
||||
bool remove_collection (collection_iterator iter, bool with_files = false);
|
||||
|
||||
/**
|
||||
* @brief Gets the grains (leaf nodes) which are members of this collection (begin iterator)
|
||||
*/
|
||||
grain_iterator begin_grains () const
|
||||
{
|
||||
return m_grains.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the grains (leaf nodes) which are members of this collection (end iterator)
|
||||
*/
|
||||
grain_iterator end_grains () const
|
||||
{
|
||||
return m_grains.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a grain to this collection
|
||||
*/
|
||||
void add_grain (const SaltGrain &grain);
|
||||
|
||||
/**
|
||||
* @brief Removes the grain given by the grain iterator
|
||||
* If "with_files" is true, also the files and the folder will be removed.
|
||||
* @return true, if the remove was successful.
|
||||
*/
|
||||
bool remove_grain (grain_iterator iter, bool with_files = false);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the collection is empty
|
||||
*/
|
||||
bool is_empty () const;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the collection is read-only
|
||||
*/
|
||||
bool is_readonly () const;
|
||||
|
||||
/**
|
||||
* @brief Loads the grain collection from the given path
|
||||
*/
|
||||
void load (const std::string &p);
|
||||
|
||||
/**
|
||||
* @brief Loads the grain collection from the given input stream
|
||||
*/
|
||||
void load (tl::InputStream &p);
|
||||
|
||||
/**
|
||||
* @brief Saves the grain collection to the given file
|
||||
*/
|
||||
void save (const std::string &p) const;
|
||||
|
||||
/**
|
||||
* @brief Scan grains from a given path
|
||||
* This will scan the grains found within this path and return a collection containing
|
||||
* the grains from this path.
|
||||
* Sub-collections are created from folders which contain grains or sub-collections.
|
||||
*/
|
||||
static SaltGrains from_path (const std::string &path, const std::string &pfx = std::string ());
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_title;
|
||||
std::string m_path;
|
||||
collections_type m_collections;
|
||||
grains_type m_grains;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,643 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltManagerDialog.h"
|
||||
#include "laySaltModel.h"
|
||||
#include "laySaltGrainPropertiesDialog.h"
|
||||
#include "laySaltDownloadManager.h"
|
||||
#include "laySalt.h"
|
||||
#include "ui_SaltGrainTemplateSelectionDialog.h"
|
||||
#include "tlString.h"
|
||||
#include "tlExceptions.h"
|
||||
#include "tlHttpStream.h"
|
||||
|
||||
#include <QTextDocument>
|
||||
#include <QPainter>
|
||||
#include <QDir>
|
||||
#include <QTextStream>
|
||||
#include <QBuffer>
|
||||
#include <QResource>
|
||||
#include <QMessageBox>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief A tiny dialog to select a template and a name for the grain
|
||||
*/
|
||||
class SaltGrainTemplateSelectionDialog
|
||||
: public QDialog, private Ui::SaltGrainTemplateSelectionDialog
|
||||
{
|
||||
public:
|
||||
SaltGrainTemplateSelectionDialog (QWidget *parent, lay::Salt *salt)
|
||||
: QDialog (parent), mp_salt (salt)
|
||||
{
|
||||
Ui::SaltGrainTemplateSelectionDialog::setupUi (this);
|
||||
|
||||
m_salt_templates.add_location (":/salt_templates");
|
||||
salt_view->setModel (new SaltModel (this, &m_salt_templates));
|
||||
salt_view->setItemDelegate (new SaltItemDelegate (this));
|
||||
salt_view->setCurrentIndex (salt_view->model ()->index (0, 0, QModelIndex ()));
|
||||
}
|
||||
|
||||
lay::SaltGrain templ () const
|
||||
{
|
||||
SaltModel *model = dynamic_cast<SaltModel *> (salt_view->model ());
|
||||
tl_assert (model != 0);
|
||||
|
||||
SaltGrain *g = model->grain_from_index (salt_view->currentIndex ());
|
||||
tl_assert (g != 0);
|
||||
|
||||
return *g;
|
||||
}
|
||||
|
||||
std::string name () const
|
||||
{
|
||||
return tl::to_string (name_edit->text ());
|
||||
}
|
||||
|
||||
void accept ()
|
||||
{
|
||||
name_alert->clear ();
|
||||
std::string name = tl::to_string (name_edit->text ().simplified ());
|
||||
if (name.empty ()) {
|
||||
name_alert->error () << tr ("Name must not be empty");
|
||||
} else if (! SaltGrain::valid_name (name)) {
|
||||
name_alert->error () << tr ("Name is not valid (must be composed of letters, digits or underscores.\nGroups and names need to be separated with slashes.");
|
||||
} else {
|
||||
|
||||
// check, if this name does not exist yet
|
||||
for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) {
|
||||
if ((*g)->name () == name) {
|
||||
name_alert->error () << tr ("A package with this name already exists");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QDialog::accept ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
lay::Salt m_salt_templates;
|
||||
lay::Salt *mp_salt;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SaltManager implementation
|
||||
|
||||
// @@@
|
||||
lay::Salt salt;
|
||||
static bool salt_initialized = false;
|
||||
void make_salt ()
|
||||
{
|
||||
if (!salt_initialized) {
|
||||
salt_initialized = true;
|
||||
salt.add_location (tl::to_string (QDir::homePath () + QString::fromUtf8("/.klayout/salt")));
|
||||
}
|
||||
}
|
||||
lay::Salt *get_salt ()
|
||||
{
|
||||
salt = lay::Salt (); salt_initialized = false;
|
||||
make_salt ();
|
||||
return &salt;
|
||||
}
|
||||
// @@@
|
||||
|
||||
// @@@
|
||||
lay::Salt salt_mine;
|
||||
void make_salt_mine ()
|
||||
{
|
||||
salt_mine = lay::Salt ();
|
||||
salt_mine.load ("/home/matthias/salt.mine");
|
||||
}
|
||||
lay::Salt *get_salt_mine ()
|
||||
{
|
||||
make_salt_mine();
|
||||
return &salt_mine;
|
||||
}
|
||||
// @@@
|
||||
|
||||
SaltManagerDialog::SaltManagerDialog (QWidget *parent)
|
||||
: QDialog (parent),
|
||||
m_current_changed_enabled (true), dm_update_models (this, &SaltManagerDialog::update_models)
|
||||
{
|
||||
Ui::SaltManagerDialog::setupUi (this);
|
||||
mp_properties_dialog = new lay::SaltGrainPropertiesDialog (this);
|
||||
|
||||
connect (edit_button, SIGNAL (clicked ()), this, SLOT (edit_properties ()));
|
||||
connect (create_button, SIGNAL (clicked ()), this, SLOT (create_grain ()));
|
||||
connect (delete_button, SIGNAL (clicked ()), this, SLOT (delete_grain ()));
|
||||
connect (apply_button, SIGNAL (clicked ()), this, SLOT (apply ()));
|
||||
|
||||
mp_salt = get_salt ();
|
||||
mp_salt_mine = get_salt_mine ();
|
||||
|
||||
SaltModel *model = new SaltModel (this, mp_salt);
|
||||
salt_view->setModel (model);
|
||||
salt_view->setItemDelegate (new SaltItemDelegate (this));
|
||||
|
||||
SaltModel *mine_model = new SaltModel (this, mp_salt_mine);
|
||||
salt_mine_view->setModel (mine_model);
|
||||
salt_mine_view->setItemDelegate (new SaltItemDelegate (this));
|
||||
|
||||
mode_tab->setCurrentIndex (mp_salt->is_empty () ? 1 : 0);
|
||||
|
||||
connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (mode_changed ()));
|
||||
|
||||
connect (mp_salt, SIGNAL (collections_changed ()), this, SLOT (salt_changed ()));
|
||||
connect (mp_salt_mine, SIGNAL (collections_changed ()), this, SLOT (salt_mine_changed ()));
|
||||
|
||||
update_models ();
|
||||
|
||||
connect (salt_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_changed ()));
|
||||
connect (salt_view, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (edit_properties ()));
|
||||
connect (salt_mine_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (mine_current_changed ()), Qt::QueuedConnection);
|
||||
connect (salt_mine_view, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (mark_clicked ()));
|
||||
|
||||
search_installed_edit->set_clear_button_enabled (true);
|
||||
search_new_edit->set_clear_button_enabled (true);
|
||||
connect (search_installed_edit, SIGNAL (textChanged (const QString &)), this, SLOT (search_text_changed (const QString &)));
|
||||
connect (search_new_edit, SIGNAL (textChanged (const QString &)), this, SLOT (search_text_changed (const QString &)));
|
||||
|
||||
connect (mark_button, SIGNAL (clicked ()), this, SLOT (mark_clicked ()));
|
||||
|
||||
salt_mine_view->addAction (actionUnmarkAll);
|
||||
QAction *a = new QAction (this);
|
||||
a->setSeparator (true);
|
||||
salt_mine_view->addAction (a);
|
||||
salt_mine_view->addAction (actionShowMarkedOnly);
|
||||
salt_mine_view->addAction (actionShowAll);
|
||||
salt_mine_view->setContextMenuPolicy (Qt::ActionsContextMenu);
|
||||
|
||||
connect (actionUnmarkAll, SIGNAL (triggered ()), this, SLOT (unmark_all ()));
|
||||
connect (actionShowMarkedOnly, SIGNAL (triggered ()), this, SLOT (show_marked_only ()));
|
||||
connect (actionShowAll, SIGNAL (triggered ()), this, SLOT (show_all ()));
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::mode_changed ()
|
||||
{
|
||||
// keeps the splitters in sync
|
||||
if (mode_tab->currentIndex () == 1) {
|
||||
splitter_new->setSizes (splitter->sizes ());
|
||||
show_all ();
|
||||
} else if (mode_tab->currentIndex () == 0) {
|
||||
splitter->setSizes (splitter_new->sizes ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::show_all ()
|
||||
{
|
||||
search_new_edit->clear ();
|
||||
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
if (! model) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
|
||||
--i;
|
||||
salt_mine_view->setRowHidden (i, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::show_marked_only ()
|
||||
{
|
||||
search_new_edit->clear ();
|
||||
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
if (! model) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
|
||||
--i;
|
||||
SaltGrain *g = model->grain_from_index (model->index (i, 0, QModelIndex ()));
|
||||
salt_mine_view->setRowHidden (i, !(g && model->is_marked (g->name ())));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::unmark_all ()
|
||||
{
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
if (model) {
|
||||
model->clear_marked ();
|
||||
update_apply_state ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::search_text_changed (const QString &text)
|
||||
{
|
||||
QListView *view = 0;
|
||||
if (sender () == search_installed_edit) {
|
||||
view = salt_view;
|
||||
} else if (sender () == search_new_edit) {
|
||||
view = salt_mine_view;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (view->model ());
|
||||
if (! model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.isEmpty ()) {
|
||||
|
||||
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
|
||||
--i;
|
||||
view->setRowHidden (i, false);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
QRegExp re (text, Qt::CaseInsensitive);
|
||||
|
||||
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
|
||||
--i;
|
||||
QModelIndex index = model->index (i, 0, QModelIndex ());
|
||||
SaltGrain *g = model->grain_from_index (index);
|
||||
bool hidden = (!g || re.indexIn (tl::to_qstring (g->name ())) < 0);
|
||||
view->setRowHidden (i, hidden);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::mark_clicked ()
|
||||
{
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
if (! model) {
|
||||
return;
|
||||
}
|
||||
SaltGrain *g = mine_current_grain ();
|
||||
if (! g) {
|
||||
return;
|
||||
}
|
||||
|
||||
model->set_marked (g->name (), !model->is_marked (g->name ()));
|
||||
update_apply_state ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::update_apply_state ()
|
||||
{
|
||||
int marked = 0;
|
||||
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
if (! model) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
|
||||
--i;
|
||||
QModelIndex index = model->index (i, 0, QModelIndex ());
|
||||
SaltGrain *g = model->grain_from_index (index);
|
||||
if (g && model->is_marked (g->name ())) {
|
||||
marked += 1;
|
||||
}
|
||||
}
|
||||
|
||||
apply_button->setEnabled (marked > 0);
|
||||
if (marked == 0) {
|
||||
apply_label->setText (QString ());
|
||||
} else if (marked == 1) {
|
||||
apply_label->setText (tr ("One package selected"));
|
||||
} else if (marked > 1) {
|
||||
apply_label->setText (tr ("%1 packages selected").arg (marked));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::apply ()
|
||||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
lay::SaltDownloadManager manager;
|
||||
|
||||
bool any = false;
|
||||
|
||||
// fetch all marked grains and register for download
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
if (model) {
|
||||
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
|
||||
--i;
|
||||
QModelIndex index = model->index (i, 0, QModelIndex ());
|
||||
SaltGrain *g = model->grain_from_index (index);
|
||||
if (g && model->is_marked (g->name ())) {
|
||||
manager.register_download (g->name (), g->url (), g->version ());
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! any) {
|
||||
throw tl::Exception (tl::to_string (tr ("No packages marked for installation or update")));
|
||||
}
|
||||
|
||||
manager.compute_dependencies (*mp_salt, *mp_salt_mine);
|
||||
|
||||
if (manager.show_confirmation_dialog (this, *mp_salt)) {
|
||||
unmark_all ();
|
||||
manager.execute (*mp_salt);
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::edit_properties ()
|
||||
{
|
||||
SaltGrain *g = current_grain ();
|
||||
if (g) {
|
||||
if (mp_properties_dialog->exec_dialog (g, mp_salt)) {
|
||||
current_changed ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::create_grain ()
|
||||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
SaltGrainTemplateSelectionDialog temp_dialog (this, mp_salt);
|
||||
if (temp_dialog.exec ()) {
|
||||
|
||||
SaltGrain target;
|
||||
target.set_name (temp_dialog.name ());
|
||||
|
||||
if (mp_salt->create_grain (temp_dialog.templ (), target)) {
|
||||
|
||||
// select the new one
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_view->model ());
|
||||
if (model) {
|
||||
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
|
||||
--i;
|
||||
QModelIndex index = model->index (i, 0, QModelIndex ());
|
||||
SaltGrain *g = model->grain_from_index (index);
|
||||
if (g && g->name () == target.name ()) {
|
||||
salt_view->setCurrentIndex (index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Initialization of new package failed - see log window (File/Log Viewer) for details")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::delete_grain ()
|
||||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
SaltGrain *g = current_grain ();
|
||||
if (! g) {
|
||||
throw tl::Exception (tl::to_string (tr ("No package selected to delete")));
|
||||
}
|
||||
|
||||
if (QMessageBox::question (this, tr ("Delete Package"), tr ("Are you sure to delete package '%1'?").arg (tl::to_qstring (g->name ())), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
mp_salt->remove_grain (*g);
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::salt_changed ()
|
||||
{
|
||||
dm_update_models ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::salt_mine_changed ()
|
||||
{
|
||||
dm_update_models ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::update_models ()
|
||||
{
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_view->model ());
|
||||
tl_assert (model != 0);
|
||||
|
||||
// NOTE: the disabling of the event handler prevents us from
|
||||
// letting the model connect to the salt's signal directly.
|
||||
m_current_changed_enabled = false;
|
||||
|
||||
model->clear_messages ();
|
||||
|
||||
// Establish a message saying that an update is available
|
||||
for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) {
|
||||
SaltGrain *gm = mp_salt_mine->grain_by_name ((*g)->name ());
|
||||
if (gm && SaltGrain::compare_versions (gm->version (), (*g)->version ()) > 0) {
|
||||
model->set_message ((*g)->name (), SaltModel::Warning, tl::to_string (tr ("An update to version %1 is available").arg (tl::to_qstring (gm->version ()))));
|
||||
}
|
||||
}
|
||||
|
||||
model->update ();
|
||||
|
||||
m_current_changed_enabled = true;
|
||||
|
||||
if (mp_salt->is_empty ()) {
|
||||
|
||||
list_stack->setCurrentIndex (1);
|
||||
details_frame->hide ();
|
||||
|
||||
} else {
|
||||
|
||||
list_stack->setCurrentIndex (0);
|
||||
details_frame->show ();
|
||||
|
||||
// select the first grain
|
||||
if (model->rowCount (QModelIndex ()) > 0) {
|
||||
salt_view->setCurrentIndex (model->index (0, 0, QModelIndex ()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SaltModel *mine_model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
tl_assert (mine_model != 0);
|
||||
|
||||
// NOTE: the disabling of the event handler prevents us from
|
||||
// letting the model connect to the salt's signal directly.
|
||||
m_current_changed_enabled = false;
|
||||
|
||||
mine_model->clear_order ();
|
||||
mine_model->clear_messages ();
|
||||
mine_model->enable_all ();
|
||||
|
||||
// Establish a message saying that an update is available
|
||||
for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) {
|
||||
SaltGrain *gm = mp_salt_mine->grain_by_name ((*g)->name ());
|
||||
if (gm && SaltGrain::compare_versions (gm->version (), (*g)->version ()) > 0) {
|
||||
mine_model->set_message ((*g)->name (), SaltModel::Warning, tl::to_string (tr ("The installed version is outdated (%1)").arg (tl::to_qstring ((*g)->version ()))));
|
||||
mine_model->set_order ((*g)->name (), -1);
|
||||
} else if (gm) {
|
||||
mine_model->set_message ((*g)->name (), SaltModel::None, tl::to_string (tr ("This package is already installed and up to date")));
|
||||
mine_model->set_order ((*g)->name (), 1);
|
||||
mine_model->set_enabled ((*g)->name (), false);
|
||||
}
|
||||
}
|
||||
|
||||
mine_model->update ();
|
||||
|
||||
m_current_changed_enabled = true;
|
||||
|
||||
// select the first grain
|
||||
if (mine_model->rowCount (QModelIndex ()) > 0) {
|
||||
salt_mine_view->setCurrentIndex (mine_model->index (0, 0, QModelIndex ()));
|
||||
}
|
||||
|
||||
mine_current_changed ();
|
||||
current_changed ();
|
||||
update_apply_state ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::current_changed ()
|
||||
{
|
||||
SaltGrain *g = current_grain ();
|
||||
details_text->set_grain (g);
|
||||
if (!g) {
|
||||
details_frame->setEnabled (false);
|
||||
delete_button->setEnabled (false);
|
||||
} else {
|
||||
details_frame->setEnabled (true);
|
||||
delete_button->setEnabled (true);
|
||||
edit_button->setEnabled (! g->is_readonly ());
|
||||
}
|
||||
}
|
||||
|
||||
lay::SaltGrain *
|
||||
SaltManagerDialog::current_grain ()
|
||||
{
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_view->model ());
|
||||
return model ? model->grain_from_index (salt_view->currentIndex ()) : 0;
|
||||
}
|
||||
|
||||
void
|
||||
SaltManagerDialog::mine_current_changed ()
|
||||
{
|
||||
|
||||
BEGIN_PROTECTED
|
||||
|
||||
SaltGrain *g = mine_current_grain ();
|
||||
details_new_frame->setEnabled (g != 0);
|
||||
|
||||
if (! g) {
|
||||
details_new_text->set_grain (0);
|
||||
return;
|
||||
}
|
||||
|
||||
m_remote_grain.reset (0);
|
||||
|
||||
// Download actual grain definition file
|
||||
try {
|
||||
|
||||
if (g->url ().empty ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("No download link available")));
|
||||
}
|
||||
|
||||
QString text = tr (
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<font color=\"#c0c0c0\">"
|
||||
"<h2>Fetching Package Definition ...</h2>"
|
||||
"<p><b>URL</b>: %1</p>"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>"
|
||||
)
|
||||
.arg (tl::to_qstring (SaltGrain::spec_url (g->url ())));
|
||||
|
||||
details_new_text->setHtml (text);
|
||||
|
||||
QApplication::processEvents (QEventLoop::ExcludeUserInputEvents);
|
||||
|
||||
tl::InputHttpStream http (SaltGrain::spec_url (g->url ()));
|
||||
tl::InputStream stream (http);
|
||||
|
||||
m_remote_grain.reset (new SaltGrain ());
|
||||
m_remote_grain->load (stream);
|
||||
m_remote_grain->set_url (g->url ());
|
||||
|
||||
if (g->name () != m_remote_grain->name ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Name mismatch between repository and actual package (repository: %1, package: %2)").arg (tl::to_qstring (g->name ())).arg (tl::to_qstring (m_remote_grain->name ()))));
|
||||
}
|
||||
if (SaltGrain::compare_versions (g->version (), m_remote_grain->version ()) != 0) {
|
||||
throw tl::Exception (tl::to_string (tr ("Version mismatch between repository and actual package (repository: %1, package: %2)").arg (tl::to_qstring (g->version ())).arg (tl::to_qstring (m_remote_grain->version ()))));
|
||||
}
|
||||
|
||||
details_new_text->set_grain (m_remote_grain.get ());
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
|
||||
m_remote_grain.reset (0);
|
||||
|
||||
QString text = tr (
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<font color=\"#ff0000\">"
|
||||
"<h2>Error Fetching Package Definition</h2>"
|
||||
"<p><b>URL</b>: %1</p>"
|
||||
"<p><b>Error</b>: %2</p>"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>"
|
||||
)
|
||||
.arg (tl::to_qstring (SaltGrain::spec_url (g->url ())))
|
||||
.arg (tl::to_qstring (tl::escaped_to_html (ex.msg ())));
|
||||
|
||||
details_new_text->setHtml (text);
|
||||
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
lay::SaltGrain *
|
||||
SaltManagerDialog::mine_current_grain ()
|
||||
{
|
||||
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
|
||||
return model ? model->grain_from_index (salt_mine_view->currentIndex ()) : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySaltManagerDialog
|
||||
#define HDR_laySaltManagerDialog
|
||||
|
||||
#include "ui_SaltManagerDialog.h"
|
||||
#include "tlDeferredExecution.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class Salt;
|
||||
class SaltGrain;
|
||||
class SaltGrainPropertiesDialog;
|
||||
|
||||
/**
|
||||
* @brief The dialog for managing the Salt ("Packages")
|
||||
*/
|
||||
class SaltManagerDialog
|
||||
: public QDialog, private Ui::SaltManagerDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SaltManagerDialog (QWidget *parent);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Called when the list of packages (grains) has changed
|
||||
*/
|
||||
void salt_changed ();
|
||||
|
||||
/**
|
||||
* @brief Called when the repository (salt mine) has changed
|
||||
*/
|
||||
void salt_mine_changed ();
|
||||
|
||||
/**
|
||||
* @brief Called when the currently selected package (grain) has changed
|
||||
*/
|
||||
void current_changed ();
|
||||
|
||||
/**
|
||||
* @brief Called when the currently selected package from the salt mine has changed
|
||||
*/
|
||||
void mine_current_changed ();
|
||||
|
||||
/**
|
||||
* @brief Called when the "edit" button is pressed
|
||||
*/
|
||||
void edit_properties ();
|
||||
|
||||
/**
|
||||
* @brief Called when the "mark" button is pressed
|
||||
*/
|
||||
void mark_clicked ();
|
||||
|
||||
/**
|
||||
* @brief Called when the "edit" button is pressed
|
||||
*/
|
||||
void create_grain ();
|
||||
|
||||
/**
|
||||
* @brief Called when the "delete" button is pressed
|
||||
*/
|
||||
void delete_grain ();
|
||||
|
||||
/**
|
||||
* @brief Called when the mode tab changed
|
||||
*/
|
||||
void mode_changed ();
|
||||
|
||||
/**
|
||||
* @brief Called when the "apply" button is clicked
|
||||
*/
|
||||
void apply ();
|
||||
|
||||
/**
|
||||
* @brief Called when one search text changed
|
||||
*/
|
||||
void search_text_changed (const QString &text);
|
||||
|
||||
/**
|
||||
* @brief Called to show the marked items only
|
||||
*/
|
||||
void show_marked_only ();
|
||||
|
||||
/**
|
||||
* @brief Called to show all items again
|
||||
*/
|
||||
void show_all ();
|
||||
|
||||
/**
|
||||
* @brief Called to unmark all items
|
||||
*/
|
||||
void unmark_all ();
|
||||
|
||||
private:
|
||||
Salt *mp_salt, *mp_salt_mine;
|
||||
std::auto_ptr<SaltGrain> m_remote_grain;
|
||||
bool m_current_changed_enabled;
|
||||
SaltGrainPropertiesDialog *mp_properties_dialog;
|
||||
tl::DeferredMethod<SaltManagerDialog> dm_update_models;
|
||||
|
||||
SaltGrain *current_grain ();
|
||||
SaltGrain *mine_current_grain ();
|
||||
void update_models ();
|
||||
void update_apply_state ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,411 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltModel.h"
|
||||
#include "laySalt.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QPainter>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QApplication>
|
||||
#include <QListView>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
SaltItemDelegate::SaltItemDelegate (QObject *parent)
|
||||
: QStyledItemDelegate (parent)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
SaltItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItemV4 optionV4 = option;
|
||||
initStyleOption (&optionV4, index);
|
||||
|
||||
bool is_enabled = (optionV4.state & QStyle::State_Enabled);
|
||||
optionV4.state |= QStyle::State_Enabled;
|
||||
|
||||
QStyle *style = optionV4.widget ? optionV4.widget->style () : QApplication::style ();
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setHtml (optionV4.text);
|
||||
|
||||
optionV4.text = QString ();
|
||||
style->drawControl (QStyle::CE_ItemViewItem, &optionV4, painter);
|
||||
|
||||
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||
|
||||
if (optionV4.state & QStyle::State_Selected) {
|
||||
ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Active, QPalette::HighlightedText));
|
||||
} else if (! is_enabled) {
|
||||
ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Disabled, QPalette::Text));
|
||||
}
|
||||
|
||||
QRect textRect = style->subElementRect (QStyle::SE_ItemViewItemText, &optionV4);
|
||||
painter->save ();
|
||||
painter->translate (textRect.topLeft ());
|
||||
painter->setClipRect (textRect.translated (-textRect.topLeft ()));
|
||||
doc.documentLayout()->draw (painter, ctx);
|
||||
painter->restore ();
|
||||
}
|
||||
|
||||
QSize
|
||||
SaltItemDelegate::sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
const int textWidth = 500;
|
||||
|
||||
QStyleOptionViewItemV4 optionV4 = option;
|
||||
initStyleOption (&optionV4, index);
|
||||
|
||||
const QListView *view = dynamic_cast<const QListView *> (optionV4.widget);
|
||||
QSize icon_size (0, 0);
|
||||
if (view) {
|
||||
icon_size = view->iconSize ();
|
||||
}
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setHtml (optionV4.text);
|
||||
doc.setTextWidth (textWidth);
|
||||
return QSize (textWidth + icon_size.width () + 6, std::max (icon_size.height () + 12, int (doc.size ().height ())));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
SaltModel::SaltModel (QObject *parent, lay::Salt *salt)
|
||||
: QAbstractItemModel (parent), mp_salt (salt)
|
||||
{
|
||||
create_ordered_list ();
|
||||
}
|
||||
|
||||
Qt::ItemFlags
|
||||
SaltModel::flags (const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags f = QAbstractItemModel::flags (index);
|
||||
|
||||
const lay::SaltGrain *g = grain_from_index (index);
|
||||
if (g && ! is_enabled (g->name ())) {
|
||||
f &= ~Qt::ItemIsSelectable;
|
||||
f &= ~Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
QVariant
|
||||
SaltModel::data (const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == Qt::DisplayRole) {
|
||||
|
||||
const lay::SaltGrain *g = grain_from_index (index);
|
||||
if (!g) {
|
||||
return QVariant ();
|
||||
}
|
||||
|
||||
std::string text = "<html><body>";
|
||||
text += "<h4>";
|
||||
text += tl::escaped_to_html (g->name ());
|
||||
if (!g->version ().empty ()) {
|
||||
text += " ";
|
||||
text += tl::escaped_to_html (g->version ());
|
||||
}
|
||||
if (!g->title ().empty ()) {
|
||||
text += " - ";
|
||||
text += tl::escaped_to_html (g->title ());
|
||||
}
|
||||
text += "</h4>";
|
||||
if (!g->doc ().empty ()) {
|
||||
text += "<p>";
|
||||
text += tl::escaped_to_html (g->doc ());
|
||||
text += "</p>";
|
||||
}
|
||||
|
||||
std::map<std::string, std::pair<Severity, std::string> >::const_iterator m = m_messages.find (g->name ());
|
||||
if (m != m_messages.end ()) {
|
||||
if (m->second.first == Warning || m->second.first == Error) {
|
||||
text += "<p><font color=\"#ff0000\"><b>" + tl::escaped_to_html (m->second.second) + "</b></font></p>";
|
||||
} else if (m->second.first == Info) {
|
||||
text += "<p><font color=\"#c0c0c0\">" + tl::escaped_to_html (m->second.second) + "</font></p>";
|
||||
} else {
|
||||
text += "<p>" + tl::escaped_to_html (m->second.second) + "</p>";
|
||||
}
|
||||
}
|
||||
|
||||
text += "</body></html>";
|
||||
|
||||
return tl::to_qstring (text);
|
||||
|
||||
} else if (role == Qt::DecorationRole) {
|
||||
|
||||
int icon_dim = 64;
|
||||
|
||||
const lay::SaltGrain *g = grain_from_index (index);
|
||||
if (!g) {
|
||||
return QVariant ();
|
||||
}
|
||||
|
||||
QImage img;
|
||||
if (g->icon ().isNull ()) {
|
||||
img = QImage (":/salt_icon.png");
|
||||
} else {
|
||||
img = g->icon ();
|
||||
}
|
||||
|
||||
if (img.width () != icon_dim || img.height () != icon_dim) {
|
||||
|
||||
QImage scaled = img.scaled (QSize (icon_dim, icon_dim), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
img = QImage (icon_dim, icon_dim, QImage::Format_ARGB32);
|
||||
img.fill (QColor (0, 0, 0, 0));
|
||||
QPainter painter (&img);
|
||||
painter.drawImage ((icon_dim - scaled.width ()) / 2, (icon_dim - scaled.height ()) / 2, scaled);
|
||||
|
||||
}
|
||||
|
||||
if (m_marked.find (g->name ()) != m_marked.end ()) {
|
||||
QPainter painter (&img);
|
||||
QImage warn (":/marked_64.png");
|
||||
painter.drawImage (0, 0, warn);
|
||||
}
|
||||
|
||||
std::map<std::string, std::pair<Severity, std::string> >::const_iterator m = m_messages.find (g->name ());
|
||||
if (m != m_messages.end ()) {
|
||||
if (m->second.first == Warning) {
|
||||
QPainter painter (&img);
|
||||
QImage warn (":/warn_16.png");
|
||||
painter.drawImage (0, 0, warn);
|
||||
} else if (m->second.first == Error) {
|
||||
QPainter painter (&img);
|
||||
QImage warn (":/error_16.png");
|
||||
painter.drawImage (0, 0, warn);
|
||||
} else if (m->second.first == Info) {
|
||||
QPainter painter (&img);
|
||||
QImage warn (":/info_16.png");
|
||||
painter.drawImage (0, 0, warn);
|
||||
}
|
||||
}
|
||||
|
||||
return QPixmap::fromImage (img);
|
||||
|
||||
} else {
|
||||
return QVariant ();
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
SaltModel::index (int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid ()) {
|
||||
return QModelIndex ();
|
||||
} else {
|
||||
return createIndex (row, column, m_ordered_grains [row]);
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
SaltModel::parent (const QModelIndex & /*index*/) const
|
||||
{
|
||||
return QModelIndex ();
|
||||
}
|
||||
|
||||
int
|
||||
SaltModel::columnCount(const QModelIndex & /*parent*/) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
SaltModel::rowCount (const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid ()) {
|
||||
return 0;
|
||||
} else {
|
||||
return int (m_ordered_grains.size ());
|
||||
}
|
||||
}
|
||||
|
||||
SaltGrain *
|
||||
SaltModel::grain_from_index (const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid ()) {
|
||||
return static_cast<SaltGrain *> (index.internalPointer ());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SaltModel::is_marked (const std::string &name) const
|
||||
{
|
||||
return m_marked.find (name) != m_marked.end ();
|
||||
}
|
||||
|
||||
bool
|
||||
SaltModel::is_enabled (const std::string &name) const
|
||||
{
|
||||
return m_disabled.find (name) == m_disabled.end ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::set_marked (const std::string &name, bool marked)
|
||||
{
|
||||
if (marked != is_marked (name)) {
|
||||
if (! marked) {
|
||||
m_marked.erase (name);
|
||||
} else {
|
||||
m_marked.insert (name);
|
||||
}
|
||||
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::clear_marked ()
|
||||
{
|
||||
if (! m_marked.empty ()) {
|
||||
m_marked.clear ();
|
||||
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::set_enabled (const std::string &name, bool enabled)
|
||||
{
|
||||
if (enabled != is_enabled (name)) {
|
||||
if (enabled) {
|
||||
m_disabled.erase (name);
|
||||
} else {
|
||||
m_disabled.insert (name);
|
||||
}
|
||||
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::enable_all ()
|
||||
{
|
||||
if (! m_disabled.empty ()) {
|
||||
m_disabled.clear ();
|
||||
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::clear_order ()
|
||||
{
|
||||
m_display_order.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::reset_order (const std::string &name)
|
||||
{
|
||||
m_display_order.erase (name);
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::set_order (const std::string &name, int order)
|
||||
{
|
||||
m_display_order[name] = order;
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::set_message (const std::string &name, Severity severity, const std::string &message)
|
||||
{
|
||||
bool needs_update = false;
|
||||
if (message.empty ()) {
|
||||
if (m_messages.find (name) != m_messages.end ()) {
|
||||
m_messages.erase (name);
|
||||
needs_update = true;
|
||||
}
|
||||
} else {
|
||||
std::map<std::string, std::pair<Severity, std::string> >::iterator m = m_messages.find (name);
|
||||
if (m == m_messages.end () || m->second.second != message || m->second.first != severity) {
|
||||
m_messages.insert (std::make_pair (name, std::make_pair (severity, message)));
|
||||
needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_update) {
|
||||
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::clear_messages ()
|
||||
{
|
||||
if (! m_messages.empty ()) {
|
||||
m_messages.clear ();
|
||||
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::update ()
|
||||
{
|
||||
create_ordered_list ();
|
||||
reset ();
|
||||
}
|
||||
|
||||
void
|
||||
SaltModel::create_ordered_list ()
|
||||
{
|
||||
m_ordered_grains.clear ();
|
||||
|
||||
if (m_display_order.empty ()) {
|
||||
|
||||
for (Salt::flat_iterator i = mp_salt->begin_flat (); i != mp_salt->end_flat (); ++i) {
|
||||
m_ordered_grains.push_back (*i);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
int min_order = m_display_order.begin ()->second;
|
||||
int max_order = min_order;
|
||||
min_order = std::min (min_order, 0);
|
||||
max_order = std::max (max_order, 0);
|
||||
|
||||
for (std::map<std::string, int>::const_iterator i = m_display_order.begin (); i != m_display_order.end (); ++i) {
|
||||
min_order = std::min (min_order, i->second);
|
||||
max_order = std::max (max_order, i->second);
|
||||
}
|
||||
|
||||
for (int o = min_order; o <= max_order; ++o) {
|
||||
for (Salt::flat_iterator i = mp_salt->begin_flat (); i != mp_salt->end_flat (); ++i) {
|
||||
std::map<std::string, int>::const_iterator d = m_display_order.find ((*i)->name ());
|
||||
int oi = 0;
|
||||
if (d != m_display_order.end ()) {
|
||||
oi = d->second;
|
||||
}
|
||||
if (oi == o) {
|
||||
m_ordered_grains.push_back (*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySaltModel
|
||||
#define HDR_laySaltModel
|
||||
|
||||
#include "layCommon.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class Salt;
|
||||
class SaltGrain;
|
||||
|
||||
/**
|
||||
* @brief A model representing the salt grains for a QListView
|
||||
*/
|
||||
class SaltModel
|
||||
: public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief An enum describing the severity of a message
|
||||
*/
|
||||
enum Severity
|
||||
{
|
||||
None = 0,
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
Error = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SaltModel (QObject *parent, lay::Salt *salt);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
QVariant data (const QModelIndex &index, int role) const;
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
Qt::ItemFlags flags (const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
QModelIndex index (int row, int column, const QModelIndex &parent) const;
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
QModelIndex parent (const QModelIndex & /*index*/) const;
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
int columnCount(const QModelIndex & /*parent*/) const;
|
||||
|
||||
/**
|
||||
* @brief Implementation of the QAbstractItemModel interface
|
||||
*/
|
||||
int rowCount (const QModelIndex &parent) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the grain pointer from a model index
|
||||
*/
|
||||
SaltGrain *grain_from_index (const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @brief Updates the model
|
||||
* Needs to be called when the salt has changed.
|
||||
*/
|
||||
void update ();
|
||||
|
||||
/**
|
||||
* @brief Sets or resets the "marked" flag on the grain with the given name
|
||||
*/
|
||||
void set_marked (const std::string &name, bool marked);
|
||||
|
||||
/**
|
||||
* @brief Clears the marked state of all grains
|
||||
*/
|
||||
void clear_marked ();
|
||||
|
||||
/**
|
||||
* @brief Enables or disables the grain with the given name
|
||||
*/
|
||||
void set_enabled (const std::string &name, bool enabled);
|
||||
|
||||
/**
|
||||
* @brief Enables all grains
|
||||
*/
|
||||
void enable_all ();
|
||||
|
||||
/**
|
||||
* @brief Installs a message on the grain with the given name
|
||||
* Installing an empty message basically removes the message.
|
||||
*/
|
||||
void set_message (const std::string &name, Severity severity, const std::string &message);
|
||||
|
||||
/**
|
||||
* @brief Removes a message
|
||||
*/
|
||||
void reset_message (const std::string &name)
|
||||
{
|
||||
set_message (name, None, std::string ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears all messages
|
||||
*/
|
||||
void clear_messages ();
|
||||
|
||||
/**
|
||||
* @brief Sets the display order
|
||||
* Specifying a display order for a name will make the grain appear
|
||||
* before or after other grains.
|
||||
* "update" needs to be called before the order becomes active.
|
||||
* Non-assigned items are considered to have order (0).
|
||||
*/
|
||||
void set_order (const std::string &name, int order);
|
||||
|
||||
/**
|
||||
* @brief Resets any display order
|
||||
*/
|
||||
void reset_order (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Resets all display order specs
|
||||
*/
|
||||
void clear_order ();
|
||||
|
||||
public:
|
||||
lay::Salt *mp_salt;
|
||||
std::set<std::string> m_marked;
|
||||
std::set<std::string> m_disabled;
|
||||
std::map<std::string, std::pair<Severity, std::string> > m_messages;
|
||||
std::map<std::string, int> m_display_order;
|
||||
std::vector<SaltGrain *> m_ordered_grains;
|
||||
|
||||
bool is_marked (const std::string &name) const;
|
||||
bool is_enabled (const std::string &name) const;
|
||||
void create_ordered_list ();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief A delegate displaying the summary of a grain
|
||||
*/
|
||||
class SaltItemDelegate
|
||||
: public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
SaltItemDelegate (QObject *parent);
|
||||
|
||||
void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<RCC>
|
||||
<qresource prefix="/salt_templates">
|
||||
</qresource>
|
||||
|
||||
<!-- font template -->
|
||||
<qresource prefix="/salt_templates/font">
|
||||
<file alias="grain.xml">salt_templates/font/grain.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/salt_templates/font/fonts">
|
||||
</qresource>
|
||||
|
||||
<!-- lib template -->
|
||||
<qresource prefix="/salt_templates/lib">
|
||||
<file alias="grain.xml">salt_templates/lib/grain.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/salt_templates/lib/libraries">
|
||||
</qresource>
|
||||
|
||||
<!-- pcell_lib template -->
|
||||
<qresource prefix="/salt_templates/pcell_lib">
|
||||
<file alias="grain.xml">salt_templates/pcell_lib/grain.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/salt_templates/pcell_lib/macros">
|
||||
</qresource>
|
||||
|
||||
<!-- macro template -->
|
||||
<qresource prefix="/salt_templates/macro">
|
||||
<file alias="grain.xml">salt_templates/macro/grain.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/salt_templates/macro/macros">
|
||||
</qresource>
|
||||
|
||||
<!-- pymacro template -->
|
||||
<qresource prefix="/salt_templates/pymacro">
|
||||
<file alias="grain.xml">salt_templates/pymacro/grain.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/salt_templates/pymacro/pymacros">
|
||||
</qresource>
|
||||
|
||||
<!-- tech template -->
|
||||
<qresource prefix="/salt_templates/tech">
|
||||
<file alias="grain.xml">salt_templates/tech/grain.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/salt_templates/tech/tech">
|
||||
</qresource>
|
||||
|
||||
</RCC>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<salt-grain>
|
||||
<name>font</name>
|
||||
<version>0.0</version>
|
||||
<title>Font package</title>
|
||||
<doc>This template provides a font for the Basic.TEXT PCell</doc>
|
||||
<doc-url/>
|
||||
<url/>
|
||||
<license>GPLv3</license>
|
||||
<author/>
|
||||
<author-contact/>
|
||||
<authored-time/>
|
||||
<installed-time/>
|
||||
<icon/>
|
||||
<screenshot/>
|
||||
</salt-grain>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<salt-grain>
|
||||
<name>lib</name>
|
||||
<version>0.0</version>
|
||||
<title>Static Library</title>
|
||||
<doc>This template provides a static library</doc>
|
||||
<doc-url/>
|
||||
<url/>
|
||||
<license>GPLv3</license>
|
||||
<author/>
|
||||
<author-contact/>
|
||||
<authored-time/>
|
||||
<installed-time/>
|
||||
<icon/>
|
||||
<screenshot/>
|
||||
</salt-grain>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<salt-grain>
|
||||
<name>macro</name>
|
||||
<version>0.0</version>
|
||||
<title>Ruby Macro</title>
|
||||
<doc>This template provides a Ruby macro</doc>
|
||||
<doc-url/>
|
||||
<url/>
|
||||
<license>GPLv3</license>
|
||||
<author/>
|
||||
<author-contact/>
|
||||
<authored-time/>
|
||||
<installed-time/>
|
||||
<icon/>
|
||||
<screenshot/>
|
||||
</salt-grain>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<salt-grain>
|
||||
<name>macro</name>
|
||||
<version>0.0</version>
|
||||
<title>PCell library</title>
|
||||
<doc>This template provides a PCell library implemented in Ruby</doc>
|
||||
<doc-url/>
|
||||
<url/>
|
||||
<license>GPLv3</license>
|
||||
<author/>
|
||||
<author-contact/>
|
||||
<authored-time/>
|
||||
<installed-time/>
|
||||
<icon/>
|
||||
<screenshot/>
|
||||
</salt-grain>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<salt-grain>
|
||||
<name>macro</name>
|
||||
<version>0.0</version>
|
||||
<title>Python Macro</title>
|
||||
<doc>This template provides a Python macro</doc>
|
||||
<doc-url/>
|
||||
<url/>
|
||||
<license>GPLv3</license>
|
||||
<author/>
|
||||
<author-contact/>
|
||||
<authored-time/>
|
||||
<installed-time/>
|
||||
<icon/>
|
||||
<screenshot/>
|
||||
</salt-grain>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<salt-grain>
|
||||
<name>macro</name>
|
||||
<version>0.0</version>
|
||||
<title>Technology</title>
|
||||
<doc>This template provides a technology</doc>
|
||||
<doc-url/>
|
||||
<url/>
|
||||
<license>GPLv3</license>
|
||||
<author/>
|
||||
<author-contact/>
|
||||
<authored-time/>
|
||||
<installed-time/>
|
||||
<icon/>
|
||||
<screenshot/>
|
||||
</salt-grain>
|
||||
|
|
@ -1820,24 +1820,6 @@ MarkerBrowserPage::set_max_marker_count (size_t max_marker_count)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
escape_to_html (std::string &out, const std::string &in)
|
||||
{
|
||||
for (const char *cp = in.c_str (); *cp; ++cp) {
|
||||
if (*cp == '<') {
|
||||
out += "<";
|
||||
} else if (*cp == '>') {
|
||||
out += ">";
|
||||
} else if (*cp == '&') {
|
||||
out += "&";
|
||||
} else if (*cp == '\n') {
|
||||
out += "<br/>";
|
||||
} else {
|
||||
out += *cp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MarkerBrowserPage::enable_updates (bool f)
|
||||
{
|
||||
|
|
@ -1955,13 +1937,13 @@ MarkerBrowserPage::update_info_text ()
|
|||
|
||||
if (category && n_category == 1 && ! category->description ().empty ()) {
|
||||
info += "<p style=\"color:blue; font-weight: bold\">";
|
||||
escape_to_html (info, category->description ());
|
||||
tl::escape_to_html (info, category->description ());
|
||||
info += "</p>";
|
||||
}
|
||||
|
||||
if (! m_error_text.empty ()) {
|
||||
info += "<p style=\"color:red; font-weight: bold\">";
|
||||
escape_to_html (info, m_error_text);
|
||||
tl::escape_to_html (info, m_error_text);
|
||||
info += "</p>";
|
||||
}
|
||||
|
||||
|
|
@ -1978,7 +1960,7 @@ MarkerBrowserPage::update_info_text ()
|
|||
if (v->tag_id () != 0) {
|
||||
const rdb::Tag &tag = mp_database->tags ().tag (v->tag_id ());
|
||||
info += "<b>";
|
||||
escape_to_html (info, tag.name ());
|
||||
tl::escape_to_html (info, tag.name ());
|
||||
info += ":</br> ";
|
||||
}
|
||||
|
||||
|
|
@ -1989,7 +1971,7 @@ MarkerBrowserPage::update_info_text ()
|
|||
value_string = std::string (value_string.begin (), value_string.begin () + max_length) + "...";
|
||||
}
|
||||
|
||||
escape_to_html (info, value_string);
|
||||
tl::escape_to_html (info, value_string);
|
||||
|
||||
info += "<br/>";
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ SOURCES = \
|
|||
tlVariant.cc \
|
||||
tlXMLParser.cc \
|
||||
tlXMLWriter.cc \
|
||||
tlFileSystemWatcher.cc
|
||||
tlFileSystemWatcher.cc \
|
||||
tlFileUtils.cc \
|
||||
tlWebDAV.cc
|
||||
|
||||
HEADERS = \
|
||||
tlAlgorithm.h \
|
||||
|
|
@ -84,7 +86,9 @@ HEADERS = \
|
|||
tlFileSystemWatcher.h \
|
||||
tlCommon.h \
|
||||
tlMath.h \
|
||||
tlCpp.h
|
||||
tlCpp.h \
|
||||
tlFileUtils.h \
|
||||
tlWebDAV.h
|
||||
|
||||
INCLUDEPATH =
|
||||
DEPENDPATH =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlInternational.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
bool
|
||||
is_parent_path (const QString &parent, const QString &path)
|
||||
{
|
||||
QFileInfo parent_info (parent);
|
||||
QFileInfo path_info (path);
|
||||
|
||||
while (parent_info != path_info) {
|
||||
path_info = path_info.path ();
|
||||
if (path_info.isRoot ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
rm_dir_recursive (const QString &path)
|
||||
{
|
||||
QDir dir (path);
|
||||
|
||||
QStringList entries = dir.entryList (QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
|
||||
for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) {
|
||||
QFileInfo fi (dir.absoluteFilePath (*e));
|
||||
if (fi.isDir ()) {
|
||||
if (! rm_dir_recursive (fi.filePath ())) {
|
||||
return false;
|
||||
}
|
||||
} else if (fi.isFile ()) {
|
||||
if (! dir.remove (*e)) {
|
||||
tl::error << QObject::tr ("Unable to remove file: %1").arg (dir.absoluteFilePath (*e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString name = dir.dirName ();
|
||||
if (dir.cdUp ()) {
|
||||
if (! dir.rmdir (name)) {
|
||||
tl::error << QObject::tr ("Unable to remove directory: %1").arg (dir.absoluteFilePath (name));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
cp_dir_recursive (const QString &source, const QString &target)
|
||||
{
|
||||
QDir dir (source);
|
||||
QDir dir_target (target);
|
||||
|
||||
QStringList entries = dir.entryList (QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
|
||||
for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) {
|
||||
|
||||
QFileInfo fi (dir.absoluteFilePath (*e));
|
||||
QFileInfo fi_target (dir_target.absoluteFilePath (*e));
|
||||
|
||||
if (fi.isDir ()) {
|
||||
|
||||
// Copy subdirectory
|
||||
if (! fi_target.exists ()) {
|
||||
if (! dir_target.mkdir (*e)) {
|
||||
tl::error << QObject::tr ("Unable to create target directory: %1").arg (dir_target.absoluteFilePath (*e));
|
||||
return false;
|
||||
}
|
||||
} else if (! fi_target.isDir ()) {
|
||||
tl::error << QObject::tr ("Unable to create target directory (is a file already): %1").arg (dir_target.absoluteFilePath (*e));
|
||||
return false;
|
||||
}
|
||||
if (! cp_dir_recursive (fi.filePath (), fi_target.filePath ())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: leave symlinks symlinks? How to copy symlinks with Qt?
|
||||
} else if (fi.isFile ()) {
|
||||
|
||||
QFile file (fi.filePath ());
|
||||
QFile file_target (fi_target.filePath ());
|
||||
|
||||
if (! file.open (QIODevice::ReadOnly)) {
|
||||
tl::error << QObject::tr ("Unable to open source file for reading: %1").arg (fi.filePath ());
|
||||
return false;
|
||||
}
|
||||
if (! file_target.open (QIODevice::WriteOnly)) {
|
||||
tl::error << QObject::tr ("Unable to open target file for writing: %1").arg (fi_target.filePath ());
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t chunk_size = 64 * 1024;
|
||||
|
||||
while (! file.atEnd ()) {
|
||||
QByteArray data = file.read (chunk_size);
|
||||
file_target.write (data);
|
||||
}
|
||||
|
||||
file.close ();
|
||||
file_target.close ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_tlFileUtils
|
||||
#define HDR_tlFileUtils
|
||||
|
||||
#include "tlCommon.h"
|
||||
#include "tlString.h"
|
||||
#include <QString>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the parent path is a parent directory of the path
|
||||
*/
|
||||
bool TL_PUBLIC is_parent_path (const QString &parent, const QString &path);
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the parent path is a parent directory of the path (version with std::string)
|
||||
*/
|
||||
inline bool TL_PUBLIC is_parent_path (const std::string &parent, const std::string &path)
|
||||
{
|
||||
return is_parent_path (tl::to_qstring (parent), tl::to_qstring (path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively remove the given directory, the files from that directory and all sub-directories
|
||||
* @return True, if successful. false otherwise.
|
||||
*/
|
||||
bool TL_PUBLIC rm_dir_recursive (const QString &path);
|
||||
|
||||
/**
|
||||
* @brief Recursively remove the given directory, the files from that directory and all sub-directories (version with std::string)
|
||||
* @return True, if successful. false otherwise.
|
||||
*/
|
||||
inline bool TL_PUBLIC rm_dir_recursive (const std::string &path)
|
||||
{
|
||||
return rm_dir_recursive (tl::to_qstring (path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively copies a given directory to a target directory
|
||||
* Both target and source directories need to exist. New directories are created in the target
|
||||
* directory if required.
|
||||
* @return True, if successful. false otherwise.
|
||||
*/
|
||||
bool TL_PUBLIC cp_dir_recursive (const QString &source, const QString &target);
|
||||
|
||||
/**
|
||||
* @brief Recursively remove the given directory, the files from that directory and all sub-directories (version with std::string)
|
||||
* @return True, if successful. false otherwise.
|
||||
*/
|
||||
inline bool TL_PUBLIC cp_dir_recursive (const std::string &source, const std::string &target)
|
||||
{
|
||||
return cp_dir_recursive (tl::to_qstring (source), tl::to_qstring (target));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -78,7 +78,7 @@ public:
|
|||
static QNetworkAccessManager *s_network_manager (0);
|
||||
|
||||
InputHttpStream::InputHttpStream (const std::string &url)
|
||||
: m_url (url)
|
||||
: m_url (url), m_request ("GET"), mp_buffer (0)
|
||||
{
|
||||
if (! s_network_manager) {
|
||||
s_network_manager = new QNetworkAccessManager(0);
|
||||
|
|
@ -87,7 +87,6 @@ InputHttpStream::InputHttpStream (const std::string &url)
|
|||
connect (s_network_manager, SIGNAL (finished (QNetworkReply *)), this, SLOT (finished (QNetworkReply *)));
|
||||
connect (s_network_manager, SIGNAL (authenticationRequired (QNetworkReply *, QAuthenticator *)), this, SLOT (authenticationRequired (QNetworkReply *, QAuthenticator *)));
|
||||
connect (s_network_manager, SIGNAL (proxyAuthenticationRequired (const QNetworkProxy &, QAuthenticator *)), this, SLOT (proxyAuthenticationRequired (const QNetworkProxy &, QAuthenticator *)));
|
||||
s_network_manager->get (QNetworkRequest (QUrl (tl::to_qstring (url))));
|
||||
mp_reply = 0;
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +96,30 @@ InputHttpStream::~InputHttpStream ()
|
|||
mp_reply = 0;
|
||||
}
|
||||
|
||||
void
|
||||
InputHttpStream::set_request (const char *r)
|
||||
{
|
||||
m_request = QByteArray (r);
|
||||
}
|
||||
|
||||
void
|
||||
InputHttpStream::set_data (const char *data)
|
||||
{
|
||||
m_data = QByteArray (data);
|
||||
}
|
||||
|
||||
void
|
||||
InputHttpStream::set_data (const char *data, size_t n)
|
||||
{
|
||||
m_data = QByteArray (data, int (n));
|
||||
}
|
||||
|
||||
void
|
||||
InputHttpStream::add_header (const std::string &name, const std::string &value)
|
||||
{
|
||||
m_headers.insert (std::make_pair (name, value));
|
||||
}
|
||||
|
||||
void
|
||||
InputHttpStream::authenticationRequired (QNetworkReply *reply, QAuthenticator *auth)
|
||||
{
|
||||
|
|
@ -117,18 +140,39 @@ InputHttpStream::finished (QNetworkReply *reply)
|
|||
QVariant redirect_target = reply->attribute (QNetworkRequest::RedirectionTargetAttribute);
|
||||
if (reply->error () == QNetworkReply::NoError && ! redirect_target.isNull ()) {
|
||||
m_url = tl::to_string (redirect_target.toString ());
|
||||
s_network_manager->get (QNetworkRequest (QUrl (redirect_target.toString ())));
|
||||
issue_request (QUrl (redirect_target.toString ()));
|
||||
delete reply;
|
||||
} else {
|
||||
mp_reply = reply;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputHttpStream::issue_request (const QUrl &url)
|
||||
{
|
||||
delete mp_buffer;
|
||||
mp_buffer = 0;
|
||||
|
||||
QNetworkRequest request (url);
|
||||
for (std::map<std::string, std::string>::const_iterator h = m_headers.begin (); h != m_headers.end (); ++h) {
|
||||
request.setRawHeader (QByteArray (h->first.c_str ()), QByteArray (h->second.c_str ()));
|
||||
}
|
||||
if (m_data.isEmpty ()) {
|
||||
s_network_manager->sendCustomRequest (request, m_request);
|
||||
} else {
|
||||
mp_buffer = new QBuffer (&m_data);
|
||||
s_network_manager->sendCustomRequest (request, m_request, mp_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
InputHttpStream::read (char *b, size_t n)
|
||||
{
|
||||
if (mp_reply == 0) {
|
||||
issue_request (QUrl (tl::to_qstring (m_url)));
|
||||
}
|
||||
while (mp_reply == 0) {
|
||||
QCoreApplication::processEvents (QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
|
||||
QCoreApplication::processEvents (QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 100);
|
||||
}
|
||||
|
||||
if (mp_reply->error () != QNetworkReply::NoError) {
|
||||
|
|
@ -160,4 +204,3 @@ InputHttpStream::filename () const
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include "tlStream.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
|
@ -68,14 +70,38 @@ public:
|
|||
*/
|
||||
virtual ~InputHttpStream ();
|
||||
|
||||
/**
|
||||
* @brief Sets the request verb
|
||||
* The default verb is "GET"
|
||||
*/
|
||||
void set_request (const char *r);
|
||||
|
||||
/**
|
||||
* @brief Sets data to be sent with the request
|
||||
* If data is given, it is sent along with the request.
|
||||
* This version takes a null-terminated string.
|
||||
*/
|
||||
void set_data (const char *data);
|
||||
|
||||
/**
|
||||
* @brief Sets data to be sent with the request
|
||||
* If data is given, it is sent along with the request.
|
||||
* This version takes a data plus length.
|
||||
*/
|
||||
void set_data (const char *data, size_t n);
|
||||
|
||||
/**
|
||||
* @brief Sets a header field
|
||||
*/
|
||||
void add_header (const std::string &name, const std::string &value);
|
||||
|
||||
/**
|
||||
* @brief Read from the stream
|
||||
*
|
||||
* Implements the basic read method.
|
||||
*/
|
||||
virtual size_t read (char *b, size_t n);
|
||||
|
||||
virtual void reset ();
|
||||
virtual void reset ();
|
||||
|
||||
virtual std::string source () const
|
||||
{
|
||||
|
|
@ -89,14 +115,20 @@ public:
|
|||
|
||||
virtual std::string filename () const;
|
||||
|
||||
private:
|
||||
std::string m_url;
|
||||
QNetworkReply *mp_reply;
|
||||
|
||||
private slots:
|
||||
void finished (QNetworkReply *);
|
||||
void authenticationRequired (QNetworkReply *, QAuthenticator *);
|
||||
void proxyAuthenticationRequired (const QNetworkProxy &, QAuthenticator *);
|
||||
|
||||
private:
|
||||
std::string m_url;
|
||||
QNetworkReply *mp_reply;
|
||||
QByteArray m_request;
|
||||
QByteArray m_data;
|
||||
QBuffer *mp_buffer;
|
||||
std::map<std::string, std::string> m_headers;
|
||||
|
||||
void issue_request (const QUrl &url);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,6 +413,46 @@ tl::to_word_or_quoted_string (const std::string &s, const char *non_term)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
tl::escape_to_html (std::string &out, const std::string &in, bool replace_newlines)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for (const char *cp = in.c_str (); *cp; ++cp) {
|
||||
if (*cp == '<') {
|
||||
out += "<";
|
||||
} else if (*cp == '>') {
|
||||
out += ">";
|
||||
} else if (*cp == '&') {
|
||||
out += "&";
|
||||
} else if (replace_newlines && *cp == '\n') {
|
||||
out += "<br/>";
|
||||
} else {
|
||||
out += *cp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
tl::escaped_to_html (const std::string &in, bool replace_newlines)
|
||||
{
|
||||
std::string s;
|
||||
escape_to_html (s, in, replace_newlines);
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
tl::from_string (const std::string &s, const char * &result)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -326,6 +326,18 @@ TL_PUBLIC int edit_distance (const std::string &a, const std::string &b);
|
|||
*/
|
||||
TL_PUBLIC std::string to_word_or_quoted_string (const std::string &s, const char *non_term = "_.$");
|
||||
|
||||
/**
|
||||
* @brief Escapes HTML (or XML) characters from in and adds the result to out
|
||||
* If "replace_newlines" is true, "\n" will be replaced by "<br/>".
|
||||
*/
|
||||
TL_PUBLIC void escape_to_html (std::string &out, const std::string &in, bool replace_newlines = true);
|
||||
|
||||
/**
|
||||
* @brief Escapes HTML (or XML) characters from in and returns the resulting string
|
||||
* If "replace_newlines" is true, "\n" will be replaced by "<br/>".
|
||||
*/
|
||||
TL_PUBLIC std::string escaped_to_html (const std::string &in, bool replace_newlines = true);
|
||||
|
||||
/**
|
||||
* @brief Set the number of digits resolution for a micron display
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,320 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "tlWebDAV.h"
|
||||
#include "tlXMLParser.h"
|
||||
#include "tlHttpStream.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// WebDAVCollection implementation
|
||||
|
||||
WebDAVObject::WebDAVObject ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A dummy "DOM" for the WebDAV reply
|
||||
*/
|
||||
struct ResourceType
|
||||
{
|
||||
ResourceType () : is_collection (false) { }
|
||||
|
||||
const std::string &collection () const
|
||||
{
|
||||
static std::string empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
void set_collection (const std::string &)
|
||||
{
|
||||
is_collection = true;
|
||||
}
|
||||
|
||||
bool is_collection;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A dummy "DOM" for the WebDAV reply
|
||||
*/
|
||||
struct Prop
|
||||
{
|
||||
ResourceType resourcetype;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A dummy "DOM" for the WebDAV reply
|
||||
*/
|
||||
struct PropStat
|
||||
{
|
||||
std::string status;
|
||||
Prop prop;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A dummy "DOM" for the WebDAV reply
|
||||
*/
|
||||
struct Response
|
||||
{
|
||||
std::string href;
|
||||
PropStat propstat;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A dummy "DOM" for the WebDAV reply
|
||||
*/
|
||||
struct MultiStatus
|
||||
{
|
||||
typedef std::list<Response> container;
|
||||
typedef container::const_iterator iterator;
|
||||
|
||||
iterator begin () const { return responses.begin (); }
|
||||
iterator end () const { return responses.end (); }
|
||||
void add (const Response &r) { responses.push_back (r); }
|
||||
|
||||
container responses;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
tl::XMLStruct<MultiStatus> xml_struct ("multistatus",
|
||||
tl::make_element (&MultiStatus::begin, &MultiStatus::end, &MultiStatus::add, "response",
|
||||
tl::make_member (&Response::href, "href") +
|
||||
tl::make_element (&Response::propstat, "propstat",
|
||||
tl::make_member (&PropStat::status, "status") +
|
||||
tl::make_element (&PropStat::prop, "prop",
|
||||
tl::make_element (&Prop::resourcetype, "resourcetype",
|
||||
tl::make_member (&ResourceType::collection, &ResourceType::set_collection, "collection")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
static std::string item_name (const QString &path1, const QString &path2)
|
||||
{
|
||||
QStringList sl1 = path1.split (QChar ('/'));
|
||||
if (! sl1.empty () && sl1.back ().isEmpty ()) {
|
||||
sl1.pop_back ();
|
||||
}
|
||||
|
||||
QStringList sl2 = path2.split (QChar ('/'));
|
||||
if (! sl2.empty () && sl2.back ().isEmpty ()) {
|
||||
sl2.pop_back ();
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for ( ; i < sl1.length () && i < sl2.length (); ++i) {
|
||||
if (sl1 [i] != sl2 [i]) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Invalid WebDAV response: %1 is not a collection item of %2").arg (path2).arg (path1)));
|
||||
}
|
||||
}
|
||||
if (i == sl2.length ()) {
|
||||
return std::string ();
|
||||
} else if (i + 1 == sl2.length ()) {
|
||||
return tl::to_string (sl2[i]);
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Invalid WebDAV response: %1 is not a collection sub-item of %2").arg (path2).arg (path1)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebDAVObject::read (const std::string &url, int depth)
|
||||
{
|
||||
QUrl base_url = QUrl (tl::to_qstring (url));
|
||||
|
||||
tl::InputHttpStream http (url);
|
||||
http.add_header ("User-Agent", "SVN");
|
||||
http.add_header ("Depth", tl::to_string (depth));
|
||||
http.set_request ("PROPFIND");
|
||||
http.set_data ("<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind xmlns=\"DAV:\"><prop><resourcetype xmlns=\"DAV:\"/></prop></propfind>");
|
||||
|
||||
MultiStatus multistatus;
|
||||
tl::InputStream stream (http);
|
||||
tl::XMLStreamSource source (stream);
|
||||
xml_struct.parse (source, multistatus);
|
||||
|
||||
// TODO: check status ..
|
||||
|
||||
m_items.clear ();
|
||||
for (MultiStatus::iterator r = multistatus.begin (); r != multistatus.end (); ++r) {
|
||||
|
||||
bool is_collection = r->propstat.prop.resourcetype.is_collection;
|
||||
QUrl item_url = base_url.resolved (QUrl (tl::to_qstring (r->href)));
|
||||
|
||||
std::string n = item_name (base_url.path (), item_url.path ());
|
||||
std::string item_url_string = tl::to_string (item_url.toString ());
|
||||
|
||||
if (! n.empty ()) {
|
||||
m_items.push_back (WebDAVItem (is_collection, item_url_string, n));
|
||||
} else {
|
||||
m_is_collection = is_collection;
|
||||
m_url = item_url_string;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct DownloadItem
|
||||
{
|
||||
DownloadItem (const std::string &u, const std::string &p)
|
||||
{
|
||||
url = u;
|
||||
path = p;
|
||||
}
|
||||
|
||||
std::string url;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
void fetch_download_items (const std::string &url, const std::string &target, std::list<DownloadItem> &items, tl::AbsoluteProgress &progress)
|
||||
{
|
||||
++progress;
|
||||
|
||||
WebDAVObject object;
|
||||
object.read (url, 1);
|
||||
|
||||
if (object.is_collection ()) {
|
||||
|
||||
QDir dir (tl::to_qstring (target));
|
||||
if (! dir.exists ()) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: target directory '%1' does not exists").arg (dir.path ())));
|
||||
}
|
||||
|
||||
for (WebDAVObject::iterator i = object.begin (); i != object.end (); ++i) {
|
||||
|
||||
QFileInfo new_item (dir.absoluteFilePath (tl::to_qstring (i->name ())));
|
||||
|
||||
if (i->is_collection ()) {
|
||||
|
||||
if (! new_item.exists ()) {
|
||||
if (! dir.mkdir (tl::to_qstring (i->name ()))) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: unable to create subdirectory '%2' in '%1'").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
|
||||
}
|
||||
} else if (! new_item.isDir ()) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: unable to create subdirectory '%2' in '%1' - is already a file").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
|
||||
} else if (! new_item.isWritable ()) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: unable to create subdirectory '%2' in '%1' - no write permissions").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
|
||||
}
|
||||
|
||||
fetch_download_items (i->url (), tl::to_string (new_item.filePath ()), items, progress);
|
||||
|
||||
} else {
|
||||
|
||||
if (new_item.exists () && ! new_item.isWritable ()) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: file is '%2' in '%1' - already exists, but no write permissions").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
|
||||
}
|
||||
|
||||
items.push_back (DownloadItem (i->url (), tl::to_string (dir.absoluteFilePath (tl::to_qstring (i->name ())))));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
items.push_back (DownloadItem (url, target));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebDAVObject::download (const std::string &url, const std::string &target)
|
||||
{
|
||||
std::list<DownloadItem> items;
|
||||
|
||||
try {
|
||||
|
||||
tl::info << QObject::tr ("Fetching file structure from ") << url;
|
||||
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Fetching directory structure from %1").arg (tl::to_qstring (url))));
|
||||
fetch_download_items (url, target, items, progress);
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << QObject::tr ("Error downloading file structure from '") << url << "':" << tl::endl << ex.msg ();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_errors = false;
|
||||
|
||||
{
|
||||
tl::info << tl::to_string (QObject::tr ("Downloading %1 file(s) now ..").arg (items.size ()));
|
||||
|
||||
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Downloading file(s) from %1").arg (tl::to_qstring (url))), items.size (), 1);
|
||||
|
||||
for (std::list<DownloadItem>::const_iterator i = items.begin (); i != items.end (); ++i) {
|
||||
|
||||
tl::info << QObject::tr ("Downloading '%1' to '%2' ..").arg (tl::to_qstring (i->url)).arg (tl::to_qstring (i->path));
|
||||
|
||||
try {
|
||||
|
||||
tl::InputHttpStream http (i->url);
|
||||
|
||||
QFile file (tl::to_qstring (i->path));
|
||||
if (! file.open (QIODevice::WriteOnly)) {
|
||||
has_errors = true;
|
||||
tl::error << QObject::tr ("Unable to open file '%1' for writing").arg (tl::to_qstring (i->path));
|
||||
}
|
||||
|
||||
const size_t chunk = 65536;
|
||||
char b[chunk];
|
||||
size_t read;
|
||||
while ((read = http.read (b, sizeof (b))) > 0) {
|
||||
if (! file.write (b, read)) {
|
||||
tl::error << QObject::tr ("Unable to write %2 bytes file '%1'").arg (tl::to_qstring (i->path)).arg (int (read));
|
||||
has_errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
file.close ();
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << QObject::tr ("Error downloading file from '") << i->url << "':" << tl::endl << ex.msg ();
|
||||
has_errors = true;
|
||||
}
|
||||
|
||||
++progress;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return ! has_errors;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_tlWebDAV
|
||||
#define HDR_tlWebDAV
|
||||
|
||||
#include "tlCommon.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Represents an item in a WebDAV collection
|
||||
*/
|
||||
class TL_PUBLIC WebDAVItem
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
WebDAVItem ()
|
||||
: m_is_collection (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
WebDAVItem (bool is_collection, const std::string &url, const std::string &name)
|
||||
: m_is_collection (is_collection), m_url (url), m_name (name)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether this item is a collection
|
||||
* If false, it's a file.
|
||||
*/
|
||||
bool is_collection () const
|
||||
{
|
||||
return m_is_collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the URL of this item
|
||||
*/
|
||||
const std::string &url () const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the name of this item
|
||||
* The name is only valid for sub-items.
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_is_collection;
|
||||
std::string m_url;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents an object from a WebDAV URL
|
||||
* This object can be a file or collection
|
||||
*/
|
||||
class TL_PUBLIC WebDAVObject
|
||||
: public WebDAVItem
|
||||
{
|
||||
public:
|
||||
typedef std::vector<WebDAVItem> container;
|
||||
typedef container::const_iterator iterator;
|
||||
|
||||
/**
|
||||
* @brief Open a stream with the given URL
|
||||
*/
|
||||
WebDAVObject ();
|
||||
|
||||
/**
|
||||
* @brief Populates the collection from the given URL
|
||||
* The depth value can be 0 (self only) or 1 (self + collection members).
|
||||
*/
|
||||
void read (const std::string &url, int depth);
|
||||
|
||||
/**
|
||||
* @brief Gets the items of this collection (begin iterator)
|
||||
*/
|
||||
iterator begin () const
|
||||
{
|
||||
return m_items.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the items of this collection (begin iterator)
|
||||
*/
|
||||
iterator end () const
|
||||
{
|
||||
return m_items.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Downloads the collection or file with the given URL
|
||||
*
|
||||
* This method will download the WebDAV object from url to the file path
|
||||
* given in "target".
|
||||
*
|
||||
* For file download, the target must be the path of the target file.
|
||||
* For collection download, the target must be a directory path. In this
|
||||
* case, the target directory must exist already.
|
||||
*
|
||||
* Sub-directories are created if required.
|
||||
*
|
||||
* This method throws an exception if the directory structure could
|
||||
* not be obtained or downloading of one file failed.
|
||||
*/
|
||||
static bool download (const std::string &url, const std::string &target);
|
||||
|
||||
private:
|
||||
container m_items;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -671,12 +671,12 @@ public:
|
|||
return m_name;
|
||||
}
|
||||
|
||||
bool check_name (const std::string &, const std::string &, const std::string &qname) const
|
||||
bool check_name (const std::string & /*uri*/, const std::string &lname, const std::string & /*qname*/) const
|
||||
{
|
||||
if (m_name == "*") {
|
||||
return true;
|
||||
} else {
|
||||
return m_name == qname; // no namespace currently
|
||||
return m_name == lname; // no namespace currently
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,359 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "laySaltGrain.h"
|
||||
#include "laySaltGrains.h"
|
||||
#include "laySalt.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "utHead.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QSignalSpy>
|
||||
|
||||
static std::string grains_to_string (const lay::SaltGrains &gg)
|
||||
{
|
||||
std::string res;
|
||||
res += "[";
|
||||
bool first = true;
|
||||
for (lay::SaltGrains::grain_iterator g = gg.begin_grains (); g != gg.end_grains (); ++g) {
|
||||
if (! first) {
|
||||
res += ",";
|
||||
}
|
||||
first = false;
|
||||
res += g->name ();
|
||||
}
|
||||
for (lay::SaltGrains::collection_iterator gc = gg.begin_collections (); gc != gg.end_collections (); ++gc) {
|
||||
if (! first) {
|
||||
res += ",";
|
||||
}
|
||||
first = false;
|
||||
res += gc->name ();
|
||||
res += grains_to_string (*gc);
|
||||
}
|
||||
res += "]";
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::string salt_to_string (lay::Salt &salt)
|
||||
{
|
||||
std::string res;
|
||||
res += "[";
|
||||
bool first = true;
|
||||
for (lay::Salt::flat_iterator i = salt.begin_flat (); i != salt.end_flat (); ++i) {
|
||||
if (! first) {
|
||||
res += ",";
|
||||
}
|
||||
first = false;
|
||||
res += (*i)->name ();
|
||||
}
|
||||
res += "]";
|
||||
return res;
|
||||
}
|
||||
|
||||
TEST (1)
|
||||
{
|
||||
std::string tmp0 = tmp_file ("tmp0");
|
||||
|
||||
lay::SaltGrain g;
|
||||
g.save (tmp0);
|
||||
EXPECT_EQ (g.authored_time ().isNull (), true);
|
||||
EXPECT_EQ (g.installed_time ().isNull (), true);
|
||||
|
||||
lay::SaltGrain g0;
|
||||
g0.load (tmp0);
|
||||
EXPECT_EQ (g0.authored_time ().isNull (), true);
|
||||
EXPECT_EQ (g0.installed_time ().isNull (), true);
|
||||
EXPECT_EQ (g == g0, true);
|
||||
|
||||
std::string tmp = tmp_file ();
|
||||
|
||||
g.set_name ("abc");
|
||||
EXPECT_EQ (g.name (), "abc");
|
||||
g.set_url ("xyz");
|
||||
EXPECT_EQ (g.url (), "xyz");
|
||||
g.set_version ("1.0");
|
||||
EXPECT_EQ (g.version (), "1.0");
|
||||
g.set_path ("a/b");
|
||||
EXPECT_EQ (g.path (), "a/b");
|
||||
g.set_title ("title");
|
||||
EXPECT_EQ (g.title (), "title");
|
||||
g.set_doc ("doc");
|
||||
EXPECT_EQ (g.doc (), "doc");
|
||||
g.set_doc_url ("doc-url");
|
||||
EXPECT_EQ (g.doc_url (), "doc-url");
|
||||
g.set_author ("me");
|
||||
EXPECT_EQ (g.author (), "me");
|
||||
g.set_author_contact ("ac");
|
||||
EXPECT_EQ (g.author_contact (), "ac");
|
||||
g.set_license ("free");
|
||||
EXPECT_EQ (g.license (), "free");
|
||||
g.set_authored_time (QDateTime ());
|
||||
EXPECT_EQ (g.authored_time ().isNull (), true);
|
||||
g.set_authored_time (QDateTime::fromMSecsSinceEpoch (1000000000));
|
||||
EXPECT_EQ (QDateTime::fromMSecsSinceEpoch (0).msecsTo (g.authored_time ()), 1000000000);
|
||||
g.set_installed_time (QDateTime ());
|
||||
EXPECT_EQ (g.installed_time ().isNull (), true);
|
||||
g.set_installed_time (QDateTime::fromMSecsSinceEpoch (2000000000));
|
||||
EXPECT_EQ (QDateTime::fromMSecsSinceEpoch (0).msecsTo (g.installed_time ()), 2000000000);
|
||||
|
||||
g.add_dependency (lay::SaltGrain::Dependency ());
|
||||
g.dependencies ().back ().name = "depname";
|
||||
g.dependencies ().back ().url = "depurl";
|
||||
g.dependencies ().back ().version = "0.0";
|
||||
EXPECT_EQ (int (g.dependencies ().size ()), 1);
|
||||
|
||||
lay::SaltGrain gg;
|
||||
EXPECT_EQ (g == gg, false);
|
||||
EXPECT_EQ (g == g, true);
|
||||
EXPECT_EQ (g != gg, true);
|
||||
EXPECT_EQ (g != g, false);
|
||||
|
||||
gg = g;
|
||||
EXPECT_EQ (g == gg, true);
|
||||
|
||||
gg.set_doc ("blabla");
|
||||
EXPECT_EQ (g == gg, false);
|
||||
|
||||
EXPECT_EQ (g == gg, false);
|
||||
g.save (tmp);
|
||||
|
||||
EXPECT_EQ (g == gg, false);
|
||||
|
||||
gg = lay::SaltGrain ();
|
||||
gg.load (tmp);
|
||||
gg.set_path (g.path ()); // path is not set by load(file)
|
||||
EXPECT_EQ (int (gg.dependencies ().size ()), 1);
|
||||
EXPECT_EQ (g == gg, true);
|
||||
|
||||
gg.add_dependency (lay::SaltGrain::Dependency ());
|
||||
EXPECT_EQ (g == gg, false);
|
||||
gg.set_path (tl::to_string (QFileInfo (tl::to_qstring (tmp)).absolutePath ()));
|
||||
gg.save ();
|
||||
|
||||
g = lay::SaltGrain::from_path (gg.path ());
|
||||
EXPECT_EQ (g == gg, true);
|
||||
}
|
||||
|
||||
TEST (2)
|
||||
{
|
||||
EXPECT_EQ (lay::SaltGrain::valid_version (""), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_version ("1"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_version ("1.2"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_version ("\t1 . 2.\n3"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_version ("x"), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_version ("1.2x"), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name (""), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x1"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x1 "), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x$1"), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x/y"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x_y"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("", ""), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", "2"), -1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", ""), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", "1"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("2", "1"), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.0", "2.0"), -1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.0", "1.0"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.1", "1.0"), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.0.1", "1.0.0"), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.0.1", "1.0"), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.0.1", "1"), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.0.0", "1"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1a", "1"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.a.1", "1.0.1"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.1a", "1.1"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.1a", "1.0"), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.1a.1", "1.0"), 1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1.1a.1", "1.1.1"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("990", "991"), -1);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("990", "990"), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("991", "990"), 1);
|
||||
}
|
||||
|
||||
TEST (3)
|
||||
{
|
||||
const QString grain_spec_file = QString::fromUtf8 ("grain.xml");
|
||||
|
||||
lay::SaltGrain g;
|
||||
g.set_name ("x");
|
||||
|
||||
QDir tmp_dir (QFileInfo (tl::to_qstring (tmp_file ())).absolutePath ());
|
||||
QDir dir_a (tmp_dir.filePath (QString::fromUtf8 ("a")));
|
||||
QDir dir_b (tmp_dir.filePath (QString::fromUtf8 ("b")));
|
||||
QDir dir_c (tmp_dir.filePath (QString::fromUtf8 ("c")));
|
||||
QDir dir_cu (dir_c.filePath (QString::fromUtf8 ("u")));
|
||||
QDir dir_cc (dir_c.filePath (QString::fromUtf8 ("c")));
|
||||
QDir dir_ccv (dir_cc.filePath (QString::fromUtf8 ("v")));
|
||||
|
||||
lay::SaltGrains gg;
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (gg.is_empty (), true);
|
||||
EXPECT_EQ (grains_to_string (gg), "[]");
|
||||
|
||||
tmp_dir.mkdir (dir_a.dirName ());
|
||||
tmp_dir.mkdir (dir_b.dirName ());
|
||||
tmp_dir.mkdir (dir_c.dirName ());
|
||||
dir_c.mkdir (dir_cu.dirName ());
|
||||
dir_c.mkdir (dir_cc.dirName ());
|
||||
dir_cc.mkdir (dir_ccv.dirName ());
|
||||
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (gg.is_empty (), true);
|
||||
EXPECT_EQ (grains_to_string (gg), "[]");
|
||||
EXPECT_EQ (gg.path (), tl::to_string (tmp_dir.path ()));
|
||||
|
||||
g.save (tl::to_string (dir_a.absoluteFilePath (grain_spec_file)));
|
||||
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (gg.is_empty (), false);
|
||||
EXPECT_EQ (grains_to_string (gg), "[a]");
|
||||
EXPECT_EQ (gg.begin_grains ()->path (), tl::to_string (dir_a.absolutePath ()));
|
||||
|
||||
g.save (tl::to_string (dir_b.absoluteFilePath (grain_spec_file)));
|
||||
g.save (tl::to_string (dir_cu.absoluteFilePath (grain_spec_file)));
|
||||
g.save (tl::to_string (dir_ccv.absoluteFilePath (grain_spec_file)));
|
||||
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (gg.is_empty (), false);
|
||||
EXPECT_EQ (grains_to_string (gg), "[a,b,c[c/u,c/c[c/c/v]]]");
|
||||
EXPECT_EQ (gg.begin_collections ()->path (), tl::to_string (dir_c.absolutePath ()));
|
||||
|
||||
std::string gg_path = tmp_file ("gg.tmp");
|
||||
gg.save (gg_path);
|
||||
|
||||
lay::SaltGrains ggg;
|
||||
ggg.load (gg_path);
|
||||
EXPECT_EQ (grains_to_string (ggg), "[a,b,c[c/u,c/c[c/c/v]]]");
|
||||
// NOTE: The path is not set, so this will fail:
|
||||
// EXPECT_EQ (gg == ggg, true);
|
||||
|
||||
gg.remove_grain (gg.begin_grains (), false);
|
||||
EXPECT_EQ (grains_to_string (gg), "[b,c[c/u,c/c[c/c/v]]]");
|
||||
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (grains_to_string (gg), "[a,b,c[c/u,c/c[c/c/v]]]");
|
||||
gg.remove_grain (gg.begin_grains (), true);
|
||||
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (grains_to_string (gg), "[b,c[c/u,c/c[c/c/v]]]");
|
||||
|
||||
gg.remove_collection (gg.begin_collections (), false);
|
||||
EXPECT_EQ (grains_to_string (gg), "[b]");
|
||||
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (grains_to_string (gg), "[b,c[c/u,c/c[c/c/v]]]");
|
||||
|
||||
gg.remove_collection (gg.begin_collections (), true);
|
||||
EXPECT_EQ (grains_to_string (gg), "[b]");
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (grains_to_string (gg), "[b]");
|
||||
}
|
||||
|
||||
TEST (4)
|
||||
{
|
||||
const QString grain_spec_file = QString::fromUtf8 ("grain.xml");
|
||||
|
||||
// That's just preparation ...
|
||||
|
||||
lay::SaltGrain g;
|
||||
g.set_name ("x");
|
||||
|
||||
QDir tmp_dir (QFileInfo (tl::to_qstring (tmp_file ())).absolutePath ());
|
||||
QDir dir_a (tmp_dir.filePath (QString::fromUtf8 ("a")));
|
||||
QDir dir_b (tmp_dir.filePath (QString::fromUtf8 ("b")));
|
||||
QDir dir_c (tmp_dir.filePath (QString::fromUtf8 ("c")));
|
||||
QDir dir_cu (dir_c.filePath (QString::fromUtf8 ("u")));
|
||||
QDir dir_cc (dir_c.filePath (QString::fromUtf8 ("c")));
|
||||
QDir dir_ccv (dir_cc.filePath (QString::fromUtf8 ("v")));
|
||||
|
||||
lay::SaltGrains gg;
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (gg.is_empty (), true);
|
||||
EXPECT_EQ (grains_to_string (gg), "[]");
|
||||
|
||||
tmp_dir.mkdir (dir_a.dirName ());
|
||||
tmp_dir.mkdir (dir_b.dirName ());
|
||||
tmp_dir.mkdir (dir_c.dirName ());
|
||||
dir_c.mkdir (dir_cu.dirName ());
|
||||
dir_c.mkdir (dir_cc.dirName ());
|
||||
dir_cc.mkdir (dir_ccv.dirName ());
|
||||
|
||||
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (gg.is_empty (), true);
|
||||
EXPECT_EQ (grains_to_string (gg), "[]");
|
||||
EXPECT_EQ (gg.path (), tl::to_string (tmp_dir.path ()));
|
||||
|
||||
g.save (tl::to_string (dir_a.absoluteFilePath (grain_spec_file)));
|
||||
g.save (tl::to_string (dir_b.absoluteFilePath (grain_spec_file)));
|
||||
g.save (tl::to_string (dir_cu.absoluteFilePath (grain_spec_file)));
|
||||
g.save (tl::to_string (dir_ccv.absoluteFilePath (grain_spec_file)));
|
||||
|
||||
// That's the main test part
|
||||
|
||||
lay::Salt salt;
|
||||
EXPECT_EQ (salt.is_empty (), true);
|
||||
|
||||
QSignalSpy spy (&salt, SIGNAL (collections_changed ()));
|
||||
EXPECT_EQ (salt_to_string (salt), "[]");
|
||||
|
||||
spy.clear ();
|
||||
salt.add_location (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (salt.is_empty (), false);
|
||||
EXPECT_EQ (spy.count (), 1);
|
||||
EXPECT_EQ (salt_to_string (salt), "[a,b,c/c/v,c/u]");
|
||||
|
||||
spy.clear ();
|
||||
salt.add_location (tl::to_string (tmp_dir.path ()));
|
||||
EXPECT_EQ (spy.count (), 0);
|
||||
EXPECT_EQ (salt_to_string (salt), "[a,b,c/c/v,c/u]");
|
||||
|
||||
spy.clear ();
|
||||
salt.add_location (tl::to_string (dir_c.path ()));
|
||||
EXPECT_EQ (spy.count (), 1);
|
||||
EXPECT_EQ (salt_to_string (salt), "[a,b,c/c/v,c/u,c/v,u]");
|
||||
|
||||
lay::Salt salt_copy = salt;
|
||||
(const_cast<lay::SaltGrains &> (*salt_copy.begin ())).remove_grain (salt_copy.begin ()->begin_grains (), true);
|
||||
|
||||
spy.clear ();
|
||||
salt.refresh ();
|
||||
EXPECT_EQ (spy.count (), 1);
|
||||
EXPECT_EQ (salt_to_string (salt), "[b,c/c/v,c/u,c/v,u]");
|
||||
|
||||
spy.clear ();
|
||||
salt.remove_location (tl::to_string (dir_c.path ()));
|
||||
EXPECT_EQ (spy.count (), 1);
|
||||
EXPECT_EQ (salt_to_string (salt), "[b,c/c/v,c/u]");
|
||||
|
||||
spy.clear ();
|
||||
// location already removed
|
||||
salt.remove_location (tl::to_string (dir_c.path ()));
|
||||
EXPECT_EQ (spy.count (), 0);
|
||||
EXPECT_EQ (salt_to_string (salt), "[b,c/c/v,c/u]");
|
||||
|
||||
EXPECT_EQ (salt.grain_by_name ("x"), 0);
|
||||
EXPECT_EQ (salt.grain_by_name ("b")->name (), "b");
|
||||
EXPECT_EQ (salt.grain_by_name ("c/c/v")->name (), "c/c/v");
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "tlFileUtils.h"
|
||||
#include "utHead.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
|
||||
TEST (1)
|
||||
{
|
||||
EXPECT_EQ (tl::is_parent_path (std::string ("/home"), "/home/matthias"), true);
|
||||
EXPECT_EQ (tl::is_parent_path (std::string ("/home"), "/home"), true);
|
||||
EXPECT_EQ (tl::is_parent_path (std::string (""), ""), true);
|
||||
EXPECT_EQ (tl::is_parent_path (std::string ("/opt/klayout"), "/home/matthias"), false);
|
||||
EXPECT_EQ (tl::is_parent_path (std::string ("/home/klayout"), "/home/matthias"), false);
|
||||
}
|
||||
|
||||
TEST (2)
|
||||
{
|
||||
QDir tmp_dir = QFileInfo (tl::to_qstring (tmp_file ())).absoluteDir ();
|
||||
tmp_dir.mkdir (QString::fromUtf8 ("a"));
|
||||
|
||||
QDir adir = tmp_dir;
|
||||
adir.cd (QString::fromUtf8 ("a"));
|
||||
|
||||
EXPECT_EQ (adir.exists (), true);
|
||||
EXPECT_EQ (tl::rm_dir_recursive (adir.absolutePath ()), true);
|
||||
EXPECT_EQ (adir.exists (), false);
|
||||
|
||||
tmp_dir.mkdir (QString::fromUtf8 ("a"));
|
||||
EXPECT_EQ (adir.exists (), true);
|
||||
|
||||
EXPECT_EQ (tl::rm_dir_recursive (tl::to_string (adir.absolutePath ())), true);
|
||||
EXPECT_EQ (adir.exists (), false);
|
||||
|
||||
tmp_dir.mkdir (QString::fromUtf8 ("a"));
|
||||
EXPECT_EQ (adir.exists (), true);
|
||||
|
||||
adir.mkdir (QString::fromUtf8 ("b1"));
|
||||
QDir b1dir = adir;
|
||||
b1dir.cd (QString::fromUtf8 ("b1"));
|
||||
|
||||
adir.mkdir (QString::fromUtf8 ("b2"));
|
||||
QDir b2dir = adir;
|
||||
b2dir.cd (QString::fromUtf8 ("b2"));
|
||||
|
||||
{
|
||||
QFile file (b2dir.absoluteFilePath (QString::fromUtf8 ("x")));
|
||||
file.open (QIODevice::WriteOnly);
|
||||
file.write ("hello, world!\n");
|
||||
file.close ();
|
||||
}
|
||||
|
||||
{
|
||||
QFile file (b2dir.absoluteFilePath (QString::fromUtf8 ("y")));
|
||||
file.open (QIODevice::WriteOnly);
|
||||
file.write ("hello, world!\n");
|
||||
file.close ();
|
||||
}
|
||||
|
||||
EXPECT_EQ (adir.exists (), true);
|
||||
EXPECT_EQ (tl::rm_dir_recursive (adir.absolutePath ()), true);
|
||||
EXPECT_EQ (adir.exists (), false);
|
||||
EXPECT_EQ (b1dir.exists (), false);
|
||||
EXPECT_EQ (b2dir.exists (), false);
|
||||
EXPECT_EQ (QFileInfo (b2dir.absoluteFilePath (QString::fromUtf8 ("x"))).exists (), false);
|
||||
}
|
||||
|
||||
TEST (3)
|
||||
{
|
||||
QDir tmp_dir = QFileInfo (tl::to_qstring (tmp_file ())).absoluteDir ();
|
||||
|
||||
tl::rm_dir_recursive (tmp_dir.filePath (QString::fromUtf8 ("a")));
|
||||
tmp_dir.mkdir (QString::fromUtf8 ("a"));
|
||||
|
||||
QDir adir = tmp_dir;
|
||||
adir.cd (QString::fromUtf8 ("a"));
|
||||
|
||||
adir.mkdir (QString::fromUtf8 ("b1"));
|
||||
QDir b1dir = adir;
|
||||
b1dir.cd (QString::fromUtf8 ("b1"));
|
||||
|
||||
adir.mkdir (QString::fromUtf8 ("b2"));
|
||||
QDir b2dir = adir;
|
||||
b2dir.cd (QString::fromUtf8 ("b2"));
|
||||
|
||||
{
|
||||
QFile file (b2dir.absoluteFilePath (QString::fromUtf8 ("x")));
|
||||
file.open (QIODevice::WriteOnly);
|
||||
file.write ("hello, world!\n");
|
||||
file.close ();
|
||||
}
|
||||
|
||||
{
|
||||
QFile file (b2dir.absoluteFilePath (QString::fromUtf8 ("y")));
|
||||
file.open (QIODevice::WriteOnly);
|
||||
file.write ("hello, world II!\n");
|
||||
file.close ();
|
||||
}
|
||||
|
||||
tl::rm_dir_recursive (tmp_dir.filePath (QString::fromUtf8 ("acopy")));
|
||||
tmp_dir.mkdir (QString::fromUtf8 ("acopy"));
|
||||
|
||||
tl::cp_dir_recursive (tmp_dir.absoluteFilePath (QString::fromUtf8 ("a")), tmp_dir.absoluteFilePath (QString::fromUtf8 ("acopy")));
|
||||
|
||||
QDir acopydir = tmp_dir;
|
||||
EXPECT_EQ (acopydir.cd (QString::fromUtf8 ("acopy")), true);
|
||||
EXPECT_EQ (acopydir.exists (), true);
|
||||
|
||||
QDir b1copydir = acopydir;
|
||||
EXPECT_EQ (b1copydir.cd (QString::fromUtf8 ("b1")), true);
|
||||
EXPECT_EQ (b1copydir.exists (), true);
|
||||
|
||||
QDir b2copydir = acopydir;
|
||||
EXPECT_EQ (b2copydir.cd (QString::fromUtf8 ("b2")), true);
|
||||
EXPECT_EQ (b2copydir.exists (), true);
|
||||
|
||||
{
|
||||
QFile file (b2copydir.absoluteFilePath (QString::fromUtf8 ("x")));
|
||||
EXPECT_EQ (file.exists (), true);
|
||||
file.open (QIODevice::ReadOnly);
|
||||
EXPECT_EQ (file.readAll ().constData (), "hello, world!\n");
|
||||
file.close ();
|
||||
}
|
||||
|
||||
{
|
||||
QFile file (b2copydir.absoluteFilePath (QString::fromUtf8 ("y")));
|
||||
EXPECT_EQ (file.exists (), true);
|
||||
file.open (QIODevice::ReadOnly);
|
||||
EXPECT_EQ (file.readAll ().constData (), "hello, world II!\n");
|
||||
file.close ();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "tlHttpStream.h"
|
||||
#include "utHead.h"
|
||||
|
||||
static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text");
|
||||
static std::string test_url2 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir1");
|
||||
|
||||
TEST(1)
|
||||
{
|
||||
tl::InputHttpStream stream (test_url1);
|
||||
|
||||
char b[100];
|
||||
size_t n = stream.read (b, sizeof (b));
|
||||
std::string res (b, n);
|
||||
EXPECT_EQ (res, "hello, world.\n");
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
tl::InputHttpStream stream (test_url2);
|
||||
stream.add_header ("User-Agent", "SVN");
|
||||
stream.add_header ("Depth", "1");
|
||||
stream.set_request ("PROPFIND");
|
||||
stream.set_data ("<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind xmlns=\"DAV:\"><prop><resourcetype xmlns=\"DAV:\"/></prop></propfind>");
|
||||
|
||||
char b[10000];
|
||||
size_t n = stream.read (b, sizeof (b));
|
||||
std::string res (b, n);
|
||||
|
||||
EXPECT_EQ (res,
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"DAV:\">\n"
|
||||
"<D:response xmlns:lp1=\"DAV:\">\n"
|
||||
"<D:href>/svn-public/klayout-resources/trunk/testdata/dir1/</D:href>\n"
|
||||
"<D:propstat>\n"
|
||||
"<D:prop>\n"
|
||||
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n"
|
||||
"</D:prop>\n"
|
||||
"<D:status>HTTP/1.1 200 OK</D:status>\n"
|
||||
"</D:propstat>\n"
|
||||
"</D:response>\n"
|
||||
"<D:response xmlns:lp1=\"DAV:\">\n"
|
||||
"<D:href>/svn-public/klayout-resources/trunk/testdata/dir1/text</D:href>\n"
|
||||
"<D:propstat>\n"
|
||||
"<D:prop>\n"
|
||||
"<lp1:resourcetype/>\n"
|
||||
"</D:prop>\n"
|
||||
"<D:status>HTTP/1.1 200 OK</D:status>\n"
|
||||
"</D:propstat>\n"
|
||||
"</D:response>\n"
|
||||
"</D:multistatus>\n"
|
||||
);
|
||||
}
|
||||
|
|
@ -423,3 +423,20 @@ TEST(10)
|
|||
EXPECT_EQ (unescape_string (escape_string ("'a\n\003")), "'a\n\003");
|
||||
}
|
||||
|
||||
TEST(11)
|
||||
{
|
||||
std::string s;
|
||||
tl::escape_to_html (s, "x");
|
||||
EXPECT_EQ (s, "x");
|
||||
tl::escape_to_html (s, "<&>");
|
||||
EXPECT_EQ (s, "x<&>");
|
||||
s = std::string ();
|
||||
tl::escape_to_html (s, "a\nb");
|
||||
EXPECT_EQ (s, "a<br/>b");
|
||||
s = std::string ();
|
||||
tl::escape_to_html (s, "a\nb", false);
|
||||
EXPECT_EQ (s, "a\nb");
|
||||
EXPECT_EQ (tl::escaped_to_html ("x<&>"), "x<&>");
|
||||
EXPECT_EQ (tl::escaped_to_html ("a\nb"), "a<br/>b");
|
||||
EXPECT_EQ (tl::escaped_to_html ("a\nb", false), "a\nb");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "tlWebDAV.h"
|
||||
#include "utHead.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata");
|
||||
static std::string test_url2 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text");
|
||||
|
||||
static std::string collection2string (const tl::WebDAVObject &coll)
|
||||
{
|
||||
std::string s;
|
||||
for (tl::WebDAVObject::iterator c = coll.begin (); c != coll.end (); ++c) {
|
||||
if (!s.empty ()) {
|
||||
s += "\n";
|
||||
}
|
||||
if (c->is_collection ()) {
|
||||
s += "[dir] ";
|
||||
}
|
||||
s += c->name ();
|
||||
s += " ";
|
||||
s += c->url ();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
TEST(1)
|
||||
{
|
||||
tl::WebDAVObject collection;
|
||||
collection.read (test_url1, 1);
|
||||
|
||||
EXPECT_EQ (collection.is_collection (), true);
|
||||
EXPECT_EQ (collection.url (), "http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/");
|
||||
|
||||
EXPECT_EQ (collection2string (collection),
|
||||
"[dir] dir1 http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir1/\n"
|
||||
"[dir] dir2 http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir2/\n"
|
||||
"text http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text\n"
|
||||
"text2 http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text2"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
tl::WebDAVObject collection;
|
||||
collection.read (test_url1, 0);
|
||||
|
||||
EXPECT_EQ (collection.is_collection (), true);
|
||||
EXPECT_EQ (collection.url (), "http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/");
|
||||
EXPECT_EQ (collection2string (collection), "");
|
||||
}
|
||||
|
||||
TEST(3)
|
||||
{
|
||||
tl::WebDAVObject collection;
|
||||
collection.read (test_url2, 1);
|
||||
|
||||
EXPECT_EQ (collection.is_collection (), false);
|
||||
EXPECT_EQ (collection.url (), "http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text");
|
||||
EXPECT_EQ (collection2string (collection), "");
|
||||
}
|
||||
|
||||
TEST(4)
|
||||
{
|
||||
tl::WebDAVObject collection;
|
||||
collection.read (test_url2, 0);
|
||||
|
||||
EXPECT_EQ (collection.is_collection (), false);
|
||||
EXPECT_EQ (collection.url (), "http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text");
|
||||
EXPECT_EQ (collection2string (collection), "");
|
||||
}
|
||||
|
||||
TEST(5)
|
||||
{
|
||||
tl::WebDAVObject collection;
|
||||
|
||||
QDir tmp_dir (tl::to_qstring (tmp_file ("tmp")));
|
||||
EXPECT_EQ (tmp_dir.exists (), false);
|
||||
|
||||
tmp_dir.cdUp ();
|
||||
tmp_dir.mkdir (tl::to_qstring ("tmp"));
|
||||
tmp_dir.cd (tl::to_qstring ("tmp"));
|
||||
|
||||
bool res = collection.download (test_url1, tl::to_string (tmp_dir.absolutePath ()));
|
||||
EXPECT_EQ (res, true);
|
||||
|
||||
QDir dir1 (tmp_dir.absoluteFilePath (QString::fromUtf8 ("dir1")));
|
||||
QDir dir2 (tmp_dir.absoluteFilePath (QString::fromUtf8 ("dir2")));
|
||||
QDir dir21 (dir2.absoluteFilePath (QString::fromUtf8 ("dir21")));
|
||||
EXPECT_EQ (dir1.exists (), true);
|
||||
EXPECT_EQ (dir2.exists (), true);
|
||||
EXPECT_EQ (dir21.exists (), true);
|
||||
|
||||
QByteArray ba;
|
||||
|
||||
QFile text1 (dir1.absoluteFilePath (QString::fromUtf8 ("text")));
|
||||
text1.open (QIODevice::ReadOnly);
|
||||
ba = text1.read (10000);
|
||||
EXPECT_EQ (ba.constData (), "A text.\n");
|
||||
text1.close ();
|
||||
|
||||
QFile text21 (dir21.absoluteFilePath (QString::fromUtf8 ("text")));
|
||||
text21.open (QIODevice::ReadOnly);
|
||||
ba = text21.read (10000);
|
||||
EXPECT_EQ (ba.constData (), "A text II.I.\n");
|
||||
text21.close ();
|
||||
}
|
||||
|
|
@ -96,7 +96,11 @@ SOURCES = \
|
|||
tlXMLParser.cc \
|
||||
gsiTest.cc \
|
||||
tlFileSystemWatcher.cc \
|
||||
tlMath.cc
|
||||
tlMath.cc \
|
||||
laySalt.cc \
|
||||
tlFileUtils.cc \
|
||||
tlHttpStream.cc \
|
||||
tlWebDAV.cc
|
||||
|
||||
# main components:
|
||||
SOURCES += \
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "tlStaticObjects.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlSystemPaths.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "layApplication.h"
|
||||
#include "gsiExpression.h"
|
||||
#include "gsiExternalMain.h"
|
||||
|
|
@ -519,8 +520,12 @@ bool TestBase::do_test (const std::string & /*mode*/)
|
|||
{
|
||||
// Ensures the test temp directory is present
|
||||
QDir dir (testtmp ());
|
||||
QDir tmpdir (dir.absoluteFilePath (tl::to_qstring (m_testdir)));
|
||||
if (tmpdir.exists () && ! tl::rm_dir_recursive (tmpdir.absolutePath ())) {
|
||||
throw tl::Exception ("Unable to clean temporary dir: " + tl::to_string (tmpdir.absolutePath ()));
|
||||
}
|
||||
if (! dir.mkpath (tl::to_qstring (m_testdir))) {
|
||||
throw tl::Exception ("Unable to create path for temporary files: " + tl::to_string (dir.filePath (tl::to_qstring (m_test))));
|
||||
throw tl::Exception ("Unable to create path for temporary files: " + tl::to_string (tmpdir.absolutePath ()));
|
||||
}
|
||||
dir.cd (tl::to_qstring (m_testdir));
|
||||
|
||||
|
|
|
|||