Merge pull request #1933 from KLayout/wip

Wip
This commit is contained in:
Matthias Köfferlein 2024-11-30 08:23:35 +01:00 committed by GitHub
commit 637660ca69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 813 additions and 320 deletions

View File

@ -1382,9 +1382,6 @@ dragging_what (const ant::Object *robj, const db::DBox &search_dbox, ant::Servic
bool
Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::angle_constraint_type /*ac*/)
{
// cancel any pending move or drag operations, reset mp_active_ruler
ui ()->drag_cancel (); // KLUDGE: every service does this to the same service manager
clear_transient_selection ();
// choose move mode

View File

@ -1938,9 +1938,11 @@ struct SelectFilterPropertyIDs
SelectFilterPropertyIDs (LayoutQuery *q)
{
data = q->register_property ("data", LQ_variant);
expressions = q->register_property ("expressions", LQ_variant);
}
unsigned int data; // data -> An array of the selected values
unsigned int expressions; // data -> An array with the expressions
};
class DB_PUBLIC SelectFilterReportingState
@ -2037,7 +2039,16 @@ public:
}
}
virtual void reset (FilterStateBase *previous)
void get_expressions (tl::Variant &v)
{
std::vector<tl::Variant> vd;
v = tl::Variant (vd.begin (), vd.end ());
for (std::vector<tl::Expression>::const_iterator e = m_expressions.begin (); e != m_expressions.end (); ++e) {
v.push (e->text ());
}
}
virtual void reset (FilterStateBase *previous)
{
if (m_has_sorting) {
@ -2082,6 +2093,9 @@ public:
if (id == m_pids.data) {
get_data (v);
return true;
} else if (id == m_pids.expressions) {
get_expressions (v);
return true;
} else if (m_in_data_eval) {
return FilterStateBase::get_property (id, v);
} else {

View File

@ -60,7 +60,7 @@ typedef l2n_std_format::keys<true> skeys;
typedef l2n_std_format::keys<false> lkeys;
LayoutToNetlistStandardReader::LayoutToNetlistStandardReader (tl::InputStream &stream)
: m_stream (stream), m_path (stream.absolute_path ()), m_dbu (0.0),
: m_stream (stream), m_path (stream.absolute_file_path ()), m_dbu (0.0),
m_progress (tl::to_string (tr ("Reading L2N database")), 1000)
{
m_progress.set_format (tl::to_string (tr ("%.0fk lines")));

View File

@ -183,7 +183,7 @@ SpiceReaderStream::line_number () const
std::string
SpiceReaderStream::source () const
{
return mp_stream->absolute_path ();
return mp_stream->absolute_file_path ();
}
bool
@ -495,7 +495,7 @@ SpiceCircuitDict::read (tl::InputStream &stream)
m_global_net_names.clear ();
m_global_nets.clear ();
m_file_id = file_id (stream.absolute_path ());
m_file_id = file_id (stream.absolute_file_path ());
while (! at_end ()) {
read_card ();

View File

@ -479,10 +479,10 @@ std::string
Technology::correct_path (const std::string &fp) const
{
std::string bp = base_path ();
if (bp.empty ()) {
if (bp.empty () || ! tl::InputStream::is_file_path (fp) || ! tl::InputStream::is_file_path (bp)) {
return fp;
} else {
return tl::relative_path (bp, fp);
return tl::relative_path (tl::InputStream::as_file_path (bp), tl::InputStream::as_file_path (fp));
}
}
@ -494,7 +494,11 @@ Technology::load (const std::string &fn)
xml_struct.parse (source, *this);
// use the tech file's path as the default base path
set_default_base_path (tl::absolute_path (fn));
if (tl::InputStream::is_file_path (fn)) {
set_default_base_path (tl::absolute_path (fn));
} else {
set_default_base_path (std::string ());
}
set_tech_file_path (fn);
}
@ -515,10 +519,10 @@ Technology::build_effective_path (const std::string &p) const
return p;
}
if (tl::is_absolute (p)) {
if (tl::InputStream::is_absolute (p)) {
return p;
} else {
return tl::combine_path (bp, p);
return tl::InputStream::combine (bp, p);
}
}

View File

@ -1811,7 +1811,7 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) {
m_current = m_start + snap_move (p - m_start);
} else {
m_current = snap_details.snapped_point;
m_current = m_start + snap_move (snap_details.snapped_point - m_start);
mouse_cursor_from_snap_details (snap_details);
}

View File

@ -588,9 +588,6 @@ Service::mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, b
bool
Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::angle_constraint_type /*ac*/)
{
// cancel any pending move or drag operations
widget ()->drag_cancel (); // KLUDGE: every service does this to the same service manager
// compute search box
double l = catch_distance ();
db::DBox search_dbox = db::DBox (p, p).enlarged (db::DVector (l, l));

View File

@ -25,7 +25,6 @@ equals(HAVE_RUBY, "1") {
!equals(HAVE_QT, "0") {
# TODO: make buddies able to build without Qt
SUBDIRS += \
klayout_main \
lay \

View File

@ -14,74 +14,18 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="6" column="0" colspan="4">
<widget class="Line" name="line_2">
<item row="1" column="2" colspan="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="group_le"/>
</item>
<item row="7" column="0" rowspan="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Database
unit</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="14" column="0" colspan="4">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QToolButton" name="browse_pb">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="15" column="1" rowspan="2" colspan="3">
<widget class="QListWidget" name="libs_lw">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>(Use the rename button to change this)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="desc_le"/>
</item>
<item row="5" column="1" colspan="3">
<widget class="QLabel" name="label_3">
<property name="text">
<string>The base path is used to locate auxiliary files if those are specified with a relative path. If none is specified, the default path is used. The default path is the one from which a technology was imported.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</spacer>
</item>
<item row="7" column="1" colspan="3">
<widget class="QFrame" name="frame">
@ -124,122 +68,13 @@ unit</string>
</layout>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QLineEdit" name="base_path_le"/>
</item>
<item row="2" column="2" colspan="2">
<widget class="QLabel" name="label_12">
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>(Used for creating tech groups)</string>
<string>Group</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="4">
<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="15" column="0">
<widget class="QLabel" name="libs_lbl">
<property name="text">
<string>Technology
specific
libraries</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Grids</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Base path</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<spacer>
<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="10" column="1" colspan="3">
<widget class="QFrame" name="frame_2">
<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="grids_le"/>
</item>
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>µm (g1,g2,...)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="16" column="0">
<spacer name="verticalSpacer_2">
<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="13" column="1" colspan="3">
<widget class="QGroupBox" name="lyp_grp">
<property name="title">
@ -284,10 +119,61 @@ libraries</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="add_other_layers_cbx">
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="name_le">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="1" colspan="3">
<widget class="QLabel" name="label_8">
<property name="text">
<string>The default database unit is used as database unit for freshly created layouts</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="10" column="1" colspan="3">
<widget class="QFrame" name="frame_2">
<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="grids_le"/>
</item>
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>Automatically add other layers</string>
<string>µm (g1,g2,...)</string>
</property>
</widget>
</item>
@ -305,37 +191,79 @@ properties</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<item row="5" column="1" colspan="3">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="name_le">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Group</string>
</property>
</widget>
</item>
<item row="8" column="1" colspan="3">
<widget class="QLabel" name="label_8">
<property name="text">
<string>The default database unit is used as database unit for freshly created layouts</string>
<string>The base path is used to locate auxiliary files if those are specified with a relative path. If none is specified, the default path is used. The default path is the one from which a technology was imported.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="11" column="1" colspan="3">
<widget class="QLabel" name="label_15">
<property name="text">
<string>These grids are available for selection from the &quot;View&quot; menu and will override the general ones. You can declare one grid as a strong default to enforce an editing grid from this list. To do so, add an exclamation mark to the grid - e.g. &quot;0.01!,0.02,0.05&quot;.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="libs_lbl">
<property name="text">
<string>Technology
specific
libraries</string>
</property>
</widget>
</item>
<item row="15" column="0" colspan="4">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="0" colspan="4">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="9" column="0" colspan="4">
<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="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Base path</string>
</property>
</widget>
</item>
<item row="12" column="0" colspan="4">
<spacer name="verticalSpacer">
<property name="orientation">
@ -352,13 +280,85 @@ properties</string>
</property>
</spacer>
</item>
<item row="11" column="1" colspan="3">
<widget class="QLabel" name="label_15">
<item row="7" column="0" rowspan="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>These grids are available for selection from the &quot;View&quot; menu and will override the general ones. You can declare one grid as a strong default to enforce an editing grid from this list. To do so, add an exclamation mark to the grid - e.g. &quot;0.01!,0.02,0.05&quot;.</string>
<string>Database
unit</string>
</property>
<property name="wordWrap">
<bool>true</bool>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="17" column="0">
<spacer name="verticalSpacer_2">
<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="16" column="1" rowspan="2" colspan="3">
<widget class="QListWidget" name="libs_lw">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<widget class="QLabel" name="label_12">
<property name="text">
<string>(Used for creating tech groups)</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QToolButton" name="browse_pb">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Grids</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="desc_le"/>
</item>
<item row="0" column="2" colspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>(Use the rename button to change this)</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QLineEdit" name="base_path_le"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="group_le"/>
</item>
<item row="14" column="1" colspan="3">
<widget class="QCheckBox" name="add_other_layers_cbx">
<property name="text">
<string>Initialize other layers with default properties</string>
</property>
</widget>
</item>

View File

@ -2981,7 +2981,7 @@ void
MainWindow::add_mru (const std::string &fn_rel, const std::string &tech)
{
std::vector <std::pair<std::string, std::string> > new_mru;
std::string fn (tl::InputStream::absolute_path (fn_rel));
std::string fn (tl::InputStream::absolute_file_path (fn_rel));
for (auto mru = m_mru.begin (); mru != m_mru.end (); ++mru) {
if (mru->first != fn /* delete non-existing files: && tl::is_readable (mru->first) */) {
@ -3025,7 +3025,7 @@ MainWindow::add_to_other_mru (const std::string &fn_rel, const std::string &cfg)
}
std::vector <std::string> new_mru;
std::string fn (tl::InputStream::absolute_path (fn_rel));
std::string fn (tl::InputStream::absolute_file_path (fn_rel));
for (auto mru = mru_ptr->begin (); mru != mru_ptr->end (); ++mru) {
if (*mru != fn /* delete non-existing files: && tl::is_readable (*mru) */) {

View File

@ -44,6 +44,8 @@
#include <QInputDialog>
#include <QHeaderView>
#include <QRegExp>
#include <QClipboard>
#include <QMimeData>
#include <fstream>
@ -76,6 +78,15 @@ SearchReplaceResults::clear ()
m_has_more = false;
}
void
SearchReplaceResults::set_data_column_headers (const tl::Variant &v)
{
m_data_column_headers = v;
if (v.is_list ()) {
m_data_columns = std::max (v.get_list ().size (), m_data_columns);
}
}
void
SearchReplaceResults::push_back (const tl::Variant &v)
{
@ -166,7 +177,13 @@ SearchReplaceResults::headerData (int section, Qt::Orientation /*orientation*/,
{
if (role == Qt::DisplayRole) {
if (! m_data_result.empty ()) {
if (section == 0) {
if (m_data_column_headers.is_list ()) {
if (section < int (m_data_column_headers.get_list ().size ())) {
return QVariant (m_data_column_headers.get_list () [section].to_string ());
} else {
return QVariant (QString ());
}
} else if (section == 0) {
return QVariant (QObject::tr ("Value"));
} else {
return QVariant (QString ());
@ -522,11 +539,37 @@ SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index, con
edt::set_object_selection (view, sel);
}
void
void
SearchReplaceResults::export_csv_to_clipboard (const std::set<int> *rows)
{
tl::OutputMemoryStream buffer;
{
tl::OutputStream os (buffer, true /* as text */);
export_csv (os, rows);
}
#if QT_VERSION >= 0x050000
QClipboard *clipboard = QGuiApplication::clipboard();
#else
QClipboard *clipboard = QApplication::clipboard();
#endif
QMimeData *data = new QMimeData ();
data->setData (QString::fromUtf8 ("text/csv"), QByteArray (buffer.data (), buffer.size ()));
data->setText (QString::fromUtf8 (buffer.data (), buffer.size ()));
clipboard->setMimeData (data);
}
void
SearchReplaceResults::export_csv (const std::string &file, const std::set<int> *rows)
{
std::ofstream output (file.c_str ());
tl::OutputStream os (file, tl::OutputStream::OM_Auto, true /* as text */);
export_csv (os, rows);
}
void
SearchReplaceResults::export_csv (tl::OutputStream &os, const std::set<int> *rows)
{
QModelIndex parent;
size_t n_columns = columnCount (parent);
@ -534,11 +577,11 @@ SearchReplaceResults::export_csv (const std::string &file, const std::set<int> *
for (size_t c = 0; c < n_columns; ++c) {
if (c) {
output << ",";
os << ",";
}
output << escape_csv (tl::to_string (headerData (int (c), Qt::Horizontal, Qt::DisplayRole).toString ()));
os << escape_csv (tl::to_string (headerData (int (c), Qt::Horizontal, Qt::DisplayRole).toString ()));
}
output << std::endl;
os << "\n";
for (size_t r = 0; r < n_rows; ++r) {
@ -546,13 +589,13 @@ SearchReplaceResults::export_csv (const std::string &file, const std::set<int> *
for (size_t c = 0; c < n_columns; ++c) {
if (c) {
output << ",";
os << ",";
}
// TODO: optimize
output << escape_csv (tl::to_string (data (index (int (r), int (c), parent), Qt::DisplayRole).toString ()));
os << escape_csv (tl::to_string (data (index (int (r), int (c), parent), Qt::DisplayRole).toString ()));
}
output << std::endl;
os << "\n";
}
@ -843,6 +886,7 @@ SearchReplaceDialog::SearchReplaceDialog (lay::Dispatcher *root, LayoutViewBase
connect (results->header (), SIGNAL (sectionCountChanged (int, int)), this, SLOT (header_columns_changed (int, int)));
QMenu *menu = new QMenu (this);
menu->addAction (QObject::tr ("Copy to clipboard"), this, SLOT (export_csv_to_clipboard ()));
menu->addAction (QObject::tr ("To CSV file"), this, SLOT (export_csv ()));
menu->addAction (QObject::tr ("To report database"), this, SLOT (export_rdb ()));
menu->addAction (QObject::tr ("To layout"), this, SLOT (export_layout ()));
@ -851,6 +895,10 @@ SearchReplaceDialog::SearchReplaceDialog (lay::Dispatcher *root, LayoutViewBase
QAction *action;
action = new QAction (QObject::tr ("Copy to clipboard"), results);
connect (action, SIGNAL (triggered ()), this, SLOT (sel_export_csv_to_clipboard ()));
results->addAction (action);
action = new QAction (QObject::tr ("Export to CSV file"), results);
connect (action, SIGNAL (triggered ()), this, SLOT (sel_export_csv ()));
results->addAction (action);
@ -1165,6 +1213,54 @@ BEGIN_PROTECTED
END_PROTECTED
}
void
SearchReplaceDialog::sel_export_csv_to_clipboard ()
{
BEGIN_PROTECTED
std::set<int> rows;
QModelIndexList sel = results->selectionModel ()->selectedRows (0);
for (auto s = sel.begin (); s != sel.end (); ++s) {
rows.insert (s->row ());
}
m_model.export_csv_to_clipboard (&rows);
END_PROTECTED
}
void
SearchReplaceDialog::export_csv_to_clipboard ()
{
BEGIN_PROTECTED
int cv_index = m_last_query_cv_index;
const lay::CellView &cv = mp_view->cellview (cv_index);
if (! cv.is_valid ()) {
return;
}
db::LayoutQuery lq (m_last_query);
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Running query")));
progress.set_unit (100000);
progress.set_format ("Processing ..");
db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query;
}
SearchReplaceResults model;
model.begin_changes (& cv->layout ());
query_to_model (model, lq, iq, std::numeric_limits<size_t>::max (), true);
model.end_changes ();
model.export_csv_to_clipboard ();
END_PROTECTED
}
void
SearchReplaceDialog::sel_export_rdb ()
{
@ -1697,6 +1793,7 @@ SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::Layo
bool res = false;
int data_prop_id = lq.has_property ("data") ? int (lq.property_by_name ("data")) : -1;
int expressions_prop_id = lq.has_property ("expressions") ? int (lq.property_by_name ("expressions")) : -1;
int shape_prop_id = lq.has_property ("shape") ? int (lq.property_by_name ("shape")) : -1;
int layer_index_prop_id = lq.has_property ("layer_index") ? int (lq.property_by_name ("layer_index")) : -1;
int instance_prop_id = lq.has_property ("inst") ? int (lq.property_by_name ("inst")) : -1;
@ -1707,6 +1804,11 @@ SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::Layo
int parent_cell_index_prop_id = lq.has_property ("parent_cell_index") ? int (lq.property_by_name ("parent_cell_index")) : -1;
int initial_cell_index_prop_id = lq.has_property ("initial_cell_index") ? int (lq.property_by_name ("initial_cell_index")) : -1;
tl::Variant ve;
if (expressions_prop_id >= 0 && iq.get (expressions_prop_id, ve)) {
model.set_data_column_headers (ve);
}
while (! iq.at_end ()) {
if (++n > max_item_count) {

View File

@ -98,6 +98,7 @@ public:
SearchReplaceResults ();
void clear ();
void set_data_column_headers (const tl::Variant &v);
void push_back (const tl::Variant &v);
void push_back (const QueryShapeResult &v);
void push_back (const QueryInstResult &v);
@ -157,6 +158,8 @@ public:
void has_more (bool hm);
void export_csv (const std::string &file, const std::set<int> *rows = 0);
void export_csv_to_clipboard (const std::set<int> *rows = 0);
void export_csv (tl::OutputStream &os, const std::set<int> *rows = 0);
void export_layout (db::Layout &layout, const std::set<int> *rows = 0);
void export_rdb (rdb::Database &rdb, double dbu, const std::set<int> *rows = 0);
void select_items (LayoutViewBase *view, int cv_index, const std::set<int> *rows = 0);
@ -167,6 +170,7 @@ private:
std::vector<QueryInstResult> m_inst_result;
std::vector<QueryCellResult> m_cell_result;
size_t m_data_columns;
tl::Variant m_data_column_headers;
mutable int m_last_column_count;
std::map<db::cell_index_type, std::string> m_cellname_map;
std::map<unsigned int, db::LayerProperties> m_lp_map;
@ -239,10 +243,12 @@ private slots:
void cancel_exec ();
void select_items ();
void export_csv ();
void export_csv_to_clipboard ();
void export_rdb ();
void export_layout ();
void sel_select_items ();
void sel_export_csv ();
void sel_export_csv_to_clipboard ();
void sel_export_rdb ();
void sel_export_layout ();

View File

@ -66,7 +66,7 @@ Session::fetch (const lay::MainWindow &mw)
if (lh) {
m_layouts.push_back (SessionLayoutDescriptor ());
m_layouts.back ().name = *l;
m_layouts.back ().file_path = tl::InputStream::absolute_path (lh->filename ());
m_layouts.back ().file_path = tl::InputStream::absolute_file_path (lh->filename ());
m_layouts.back ().load_options = lh->load_options ();
m_layouts.back ().save_options = lh->save_options ();
m_layouts.back ().save_options_valid = lh->save_options_valid ();
@ -89,7 +89,7 @@ Session::fetch (const lay::MainWindow &mw)
const rdb::Database *rdb = view->get_rdb (j);
if (rdb && ! rdb->filename ().empty ()) {
view_desc.rdb_filenames.push_back (tl::InputStream::absolute_path (rdb->filename ()));
view_desc.rdb_filenames.push_back (tl::InputStream::absolute_file_path (rdb->filename ()));
}
}
@ -98,7 +98,7 @@ Session::fetch (const lay::MainWindow &mw)
const db::LayoutToNetlist *l2ndb = view->get_l2ndb (j);
if (l2ndb && ! l2ndb->filename ().empty ()) {
view_desc.l2ndb_filenames.push_back (tl::InputStream::absolute_path (l2ndb->filename ()));
view_desc.l2ndb_filenames.push_back (tl::InputStream::absolute_file_path (l2ndb->filename ()));
}
}

View File

@ -173,11 +173,10 @@ TechBaseEditorPage::commit ()
if (! mp_ui->lyp_grp->isChecked ()) {
tech ()->set_layer_properties_file (std::string ());
tech ()->set_add_other_layers (true);
} else {
tech ()->set_layer_properties_file (tl::to_string (mp_ui->lyp_le->text ()));
tech ()->set_add_other_layers (mp_ui->add_other_layers_cbx->isChecked ());
}
tech ()->set_add_other_layers (mp_ui->add_other_layers_cbx->isChecked ());
}
void

View File

@ -76,15 +76,16 @@ std::vector<std::pair<std::string, std::string> >
unpack_key_binding (const std::string &packed)
{
tl::Extractor ex (packed.c_str ());
ex.test(";"); // backward compatibiliy
std::vector<std::pair<std::string, std::string> > key_bindings;
while (! ex.at_end ()) {
ex.test(";");
key_bindings.push_back (std::make_pair (std::string (), std::string ()));
ex.read_word_or_quoted (key_bindings.back ().first);
ex.test(":");
ex.read_word_or_quoted (key_bindings.back ().second);
ex.test(";");
}
return key_bindings;
@ -93,17 +94,26 @@ unpack_key_binding (const std::string &packed)
std::string
pack_key_binding (const std::vector<std::pair<std::string, std::string> > &unpacked)
{
std::string packed;
std::string packed = "\n";
bool first = true;
for (std::vector<std::pair<std::string, std::string> >::const_iterator p = unpacked.begin (); p != unpacked.end (); ++p) {
if (! packed.empty ()) {
packed += ";";
// for easier editing we separate the entries into non-empty and empty ones and put each of them on a new line
for (int pass = 0; pass < 2; ++pass) {
for (std::vector<std::pair<std::string, std::string> >::const_iterator p = unpacked.begin (); p != unpacked.end (); ++p) {
if ((pass == 0) == p->second.empty ()) {
continue;
}
if (! first) {
packed += ";\n";
}
first = false;
packed += tl::to_word_or_quoted_string (p->first);
packed += ":";
packed += tl::to_word_or_quoted_string (p->second);
}
packed += tl::to_word_or_quoted_string (p->first);
packed += ":";
packed += tl::to_word_or_quoted_string (p->second);
}
packed += "\n";
return packed;
}
@ -111,15 +121,16 @@ std::vector<std::pair<std::string, bool> >
unpack_menu_items_hidden (const std::string &packed)
{
tl::Extractor ex (packed.c_str ());
ex.test(";"); // backward compatibiliy
std::vector<std::pair<std::string, bool> > hidden;
while (! ex.at_end ()) {
ex.test(";");
hidden.push_back (std::make_pair (std::string (), false));
ex.read_word_or_quoted (hidden.back ().first);
ex.test(":");
ex.read (hidden.back ().second);
ex.test(";");
}
return hidden;
@ -128,17 +139,26 @@ unpack_menu_items_hidden (const std::string &packed)
std::string
pack_menu_items_hidden (const std::vector<std::pair<std::string, bool> > &unpacked)
{
std::string packed;
std::string packed = "\n";
bool first = true;
for (std::vector<std::pair<std::string, bool> >::const_iterator p = unpacked.begin (); p != unpacked.end (); ++p) {
if (! packed.empty ()) {
packed += ";";
// for easier editing we separate the entries into true and false ones and put each of them on a new line
for (int pass = 0; pass < 2; ++pass) {
for (std::vector<std::pair<std::string, bool> >::const_iterator p = unpacked.begin (); p != unpacked.end (); ++p) {
if ((pass == 0) != p->second) {
continue;
}
if (! first) {
packed += ";\n";
}
first = false;
packed += tl::to_word_or_quoted_string (p->first);
packed += ":";
packed += tl::to_string (p->second);
}
packed += tl::to_word_or_quoted_string (p->first);
packed += ":";
packed += tl::to_string (p->second);
}
packed += "\n";
return packed;
}

View File

@ -165,9 +165,9 @@ Editables::selection_catch_bbox ()
}
void
Editables::transform (const db::DCplxTrans &t, db::Transaction *transaction)
Editables::transform (const db::DCplxTrans &t)
{
std::unique_ptr<db::Transaction> trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (tr ("Transform"))));
std::unique_ptr<db::Transaction> trans_holder (new db::Transaction (manager (), tl::to_string (tr ("Transform"))));
if (has_selection ()) {
@ -639,15 +639,31 @@ Editables::edit_cancel ()
}
}
void
Editables::edit_finish ()
{
clear_previous_selection ();
for (iterator e = begin (); e != end (); ++e) {
e->edit_finish ();
}
}
void
Editables::cancel_edits ()
{
// cancel any edit operations
for (iterator e = begin (); e != end (); ++e) {
e->edit_cancel ();
}
}
void
Editables::finish_edits ()
{
for (iterator e = begin (); e != end (); ++e) {
e->edit_finish ();
}
}
void
Editables::show_properties ()
{

View File

@ -332,6 +332,20 @@ public:
// .. by default, nothing is implemented ..
}
/**
* @brief Finishes any pending operations
*
* This event is sent whenever a pending operation such as
* a move operation should be finished.
* In contrast to "edit_cancel", this version is supposed
* not to rollback, for example if transactions are involved.
*/
virtual void edit_finish ()
{
// by default maps to edit_cancel
edit_cancel ();
}
/**
* @brief Indicates if any objects are selected
*/
@ -457,14 +471,11 @@ public:
db::DBox selection_bbox ();
/**
* @brief transform the selection
* @brief Transforms the selection
*
* The transformation is given in micron units.
*
* If a transaction is given, the operation will be appended to this pending transaction.
* The Editables object takes ownership over the Transaction object.
*/
void transform (const db::DCplxTrans &tr, db::Transaction *transaction = 0);
virtual void transform (const db::DCplxTrans &tr);
/**
* @brief Enable or disable a certain editable
@ -565,9 +576,18 @@ public:
/**
* @brief Cancel any pending operations
*
* This method calls "edit_cancel" on all services and resets selection tracking.
*/
void edit_cancel ();
/**
* @brief Finishes any pending operations
*
* This method calls "edit_finish" on all services and resets selection tracking.
*/
void edit_finish ();
/**
* @brief Editable iterator: begin
*/
@ -633,11 +653,21 @@ protected:
* @brief Cancel all edit operations
*
* This method can be overridden in order to implement special behaviour on cancel
* of edits (i.e. release the mouse).
* of edits (e.g. clean up markers).
* Make sure, the base implementation is called as well.
*/
virtual void cancel_edits ();
/**
* @brief Finishes all edit operations
*
* This method can be overridden in order to implement special behaviour on finishing
* of edits (e.g. clean up markers). In contrast to "cancel_edits", this method
* is expected not to rollback any operations - i.e. undo transactions.
* Make sure, the base implementation is called as well.
*/
virtual void finish_edits ();
private:
friend class Editable;

View File

@ -235,6 +235,8 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons
m_path.push_back (db::InstElement (*inst, p));
checkpoint ();
do_find (mp_layout->cell (cell_inst.object ().cell_index ()),
level + 1,
vp,
@ -772,6 +774,14 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db
return ! m_founds.empty ();
}
void
InstFinder::checkpoint ()
{
if (--m_tries < 0) {
throw StopException ();
}
}
void
InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::Box & /*scan_box*/, const db::DCplxTrans & /*vp*/, const db::ICplxTrans &t, int level)
{
@ -854,17 +864,13 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
} else {
if (--m_tries < 0) {
throw StopException ();
}
checkpoint ();
// look for instances to check here ..
db::Cell::touching_iterator inst = cell.begin_touching (search_box);
while (! inst.at_end ()) {
if (--m_tries < 0) {
throw StopException ();
}
checkpoint ();
const db::CellInstArray &cell_inst = inst->cell_inst ();
const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ());
@ -877,9 +883,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
db::box_convert <db::CellInst, false> bc (layout ());
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
if (--m_tries < 0) {
throw StopException ();
}
checkpoint ();
bool match = false;
double d = std::numeric_limits<double>::max ();

View File

@ -189,6 +189,11 @@ protected:
*/
void test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match);
/**
* @brief Is called "frequently", so the finder can stop after a number of tries and not waste time
*/
virtual void checkpoint () = 0;
private:
void do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t);
@ -282,7 +287,7 @@ protected:
m_tries = n;
}
void checkpoint ();
virtual void checkpoint ();
private:
virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level);
@ -339,6 +344,8 @@ public:
return m_founds.end ();
}
virtual void checkpoint ();
private:
virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level);

View File

@ -2522,7 +2522,7 @@ LayoutViewBase::signal_apply_technology (lay::LayoutHandle *layout_handle)
lyp_file = tech->eff_layer_properties_file ();
}
if (! lyp_file.empty ()) {
if (! lyp_file.empty () || tech->add_other_layers ()) {
// interpolate the layout properties file name
tl::Eval expr;
@ -3983,12 +3983,44 @@ LayoutViewBase::redraw ()
mp_canvas->redraw_new (layers);
}
void
LayoutViewBase::transform (const db::DCplxTrans &tr)
{
// NOTE: we call "finish_edits" rather than "cancel_edits" because
// "move by" while "duplicate interactive" relies on keeping the
// pasted shapes from the previous transaction. So we must not roll back.
finish_edits ();
lay::Editables::transform (tr);
}
void
LayoutViewBase::cancel_edits ()
{
// the move service takes a special role here as it manages the
// transaction for the collective move operation.
mp_move_service->cancel ();
// cancel all drag and pending edit operations such as move operations.
mp_canvas->drag_cancel ();
lay::Editables::cancel_edits ();
// re-enable edit mode
enable_edits (true);
}
void
LayoutViewBase::finish_edits ()
{
// the move service takes a special role here as it manages the
// transaction for the collective move operation.
mp_move_service->finish ();
// cancel all drag operations
mp_canvas->drag_cancel ();
lay::Editables::finish_edits ();
// re-enable edit mode
enable_edits (true);
}
void
@ -3996,8 +4028,6 @@ LayoutViewBase::cancel ()
{
// cancel all drags and pending edit operations such as move operations.
cancel_edits ();
// re-enable edit mode
enable_edits (true);
// and clear the selection
clear_selection ();
}

View File

@ -2666,6 +2666,13 @@ public:
*/
void cancel_edits ();
/**
* @brief Finishes all edit operations and maintains selection
*
* In contrast to "cancel_edits" there is no rollback of operations applied already.
*/
void finish_edits ();
/**
* @brief Select all levels of hierarchy available
*/
@ -2696,7 +2703,14 @@ public:
*/
void ensure_selection_visible ();
/**
/**
* @brief Transforms the selection
*
* The transformation is given in micron units.
*/
virtual void transform (const db::DCplxTrans &tr);
/**
* @brief Select a cell by index for a certain cell view
*
* This will be forwarded to select_cell or select_cell_fit depending

View File

@ -302,6 +302,7 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_
if (! m_dragging) {
mp_transaction.reset (trans_holder.release ());
ui ()->drag_cancel ();
if (mp_editables->begin_move (p, ac_from_buttons (buttons))) {
@ -337,21 +338,31 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_
}
void
MoveService::drag_cancel ()
{
MoveService::drag_cancel ()
{
m_shift = db::DPoint ();
if (m_dragging) {
mp_editables->edit_cancel ();
ui ()->ungrab_mouse (this);
m_dragging = false;
}
}
void
MoveService::cancel ()
{
if (m_dragging) {
if (mp_transaction.get ()) {
mp_transaction->cancel ();
}
mp_transaction.reset (0);
}
}
void
MoveService::finish ()
{
if (m_dragging) {
mp_transaction.reset (0);
}
}

View File

@ -43,6 +43,8 @@ public:
bool configure (const std::string &name, const std::string &value);
bool begin_move (db::Transaction *transaction = 0, bool transient_selection = false);
void finish ();
void cancel ();
private:
virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio);

View File

@ -276,7 +276,7 @@ public:
virtual void set_colors (tl::Color /*background*/, tl::Color /*text*/) { }
/**
* @brief This method is called when a drag operation should be cancelled
* @brief This method is called when a mouse tracking operation should be cancelled
*/
virtual void drag_cancel () { }

View File

@ -89,8 +89,8 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/back_16.png</normaloff>:/back_16.png</iconset>
<iconset resource="../../icons/icons.qrc">
<normaloff>:/back_16px.png</normaloff>:/back_16px.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
@ -103,8 +103,8 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/forward_16.png</normaloff>:/forward_16.png</iconset>
<iconset resource="../../icons/icons.qrc">
<normaloff>:/forward_16px.png</normaloff>:/forward_16px.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>

View File

@ -1199,8 +1199,7 @@ LayoutViewFunctions::do_cm_duplicate (bool interactive)
try {
bool transient_mode = ! view ()->has_selection ();
view ()->copy_view_objects ();
view ()->clear_selection ();
view ()->cancel ();
view ()->cancel_edits ();
if (interactive) {
view ()->paste_interactive (transient_mode);
} else {
@ -1217,8 +1216,7 @@ void
LayoutViewFunctions::do_cm_paste (bool interactive)
{
if (! db::Clipboard::instance ().empty ()) {
view ()->cancel ();
view ()->clear_selection ();
view ()->cancel_edits ();
if (interactive) {
view ()->paste_interactive ();
} else {
@ -1330,9 +1328,7 @@ LayoutViewFunctions::cm_reload ()
void
LayoutViewFunctions::do_transform (const db::DCplxTrans &tr)
{
// end move operations, cancel edit operations
view ()->cancel_edits ();
view ()->lay::Editables::transform (tr);
view ()->transform (tr);
}
void
@ -1545,6 +1541,7 @@ LayoutViewFunctions::cm_sel_scale ()
void
LayoutViewFunctions::cm_sel_move_interactive ()
{
view ()->cancel_edits ();
if (view ()->move_service ()->begin_move ()) {
view ()->switch_mode (-1); // move mode
}

View File

@ -1439,6 +1439,17 @@ LayoutView::cancel_edits ()
LayoutViewBase::cancel_edits ();
}
void
LayoutView::finish_edits ()
{
// closes the property dialog
if (mp_properties_dialog) {
mp_properties_dialog->hide ();
}
LayoutViewBase::finish_edits ();
}
void
LayoutView::activate ()
{

View File

@ -459,6 +459,11 @@ public:
*/
void cancel_edits ();
/**
* @brief Finishes all edit operations and maintains selection
*/
void finish_edits ();
/**
* @brief Select all levels of hierarchy available
*/

View File

@ -1084,7 +1084,7 @@ LEFDEFReaderState::read_single_map_file (const std::string &path, std::map<std::
tl::InputStream file_stream (file);
tl::TextInputStream ts (file_stream);
tl::log << tl::to_string (tr ("Reading LEF/DEF map file")) << " " << file_stream.absolute_path ();
tl::log << tl::to_string (tr ("Reading LEF/DEF map file")) << " " << file_stream.absolute_file_path ();
// Purpose name to purpose code
std::map<std::string, LayerPurpose> purpose_translation;
@ -2011,7 +2011,7 @@ LEFDEFImporter::get_mask (long m)
void
LEFDEFImporter::read (tl::InputStream &stream, db::Layout &layout, LEFDEFReaderState &state)
{
tl::log << tl::to_string (tr ("Reading LEF/DEF file")) << " " << stream.absolute_path ();
tl::log << tl::to_string (tr ("Reading LEF/DEF file")) << " " << stream.absolute_file_path ();
m_fn = stream.filename ();

View File

@ -120,7 +120,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti
std::string base_path;
if (! effective_options.paths_relative_to_cwd ()) {
base_path = tl::dirname (m_stream.absolute_path ());
base_path = tl::dirname (m_stream.absolute_file_path ());
}
db::LEFDEFReaderState state (&effective_options, layout, base_path);
@ -179,7 +179,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti
if (effective_options.read_lef_with_def ()) {
std::string input_dir = tl::absolute_path (m_stream.absolute_path ());
std::string input_dir = tl::absolute_path (m_stream.absolute_file_path ());
if (tl::file_exists (input_dir)) {

View File

@ -1798,7 +1798,7 @@ Database::load (std::string fn)
reader.read (*this);
}
set_filename (stream.absolute_path ());
set_filename (stream.absolute_file_path ());
set_name (stream.filename ());
reset_modified ();

View File

@ -4059,7 +4059,6 @@ Eval::parse (Expression &expr, const std::string &s, bool top)
expr = Expression (this, s);
tl::Extractor ex (s.c_str ());
tl::Extractor ex0 = ex;
ExpressionParserContext context (&expr, ex);
if (top) {
@ -4074,6 +4073,8 @@ Eval::parse (Expression &expr, const std::string &s, bool top)
void
Eval::parse (Expression &expr, tl::Extractor &ex, bool top)
{
ex.skip ();
expr = Expression (this, ex.get ());
tl::Extractor ex0 = ex;
@ -4093,6 +4094,8 @@ Eval::parse (Expression &expr, tl::Extractor &ex, bool top)
std::string
Eval::parse_expr (tl::Extractor &ex, bool top)
{
ex.skip ();
tl::Eval eval (0, true);
Expression expr (&eval, ex.get ());

View File

@ -254,7 +254,6 @@ static std::vector<std::string> split_filename (const std::string &fn)
const char *cp0 = cp;
++cp;
while (*cp && *cp != '.') {
// backslash escaping (ineffective on Windows because that is a path separator)
if (*cp == '\\' && cp[1]) {
++cp;
}

View File

@ -425,32 +425,124 @@ InputStream::~InputStream ()
}
}
std::string InputStream::absolute_path (const std::string &abstract_path)
std::string InputStream::absolute_file_path (const std::string &abstract_path)
{
// TODO: align this implementation with InputStream ctor
tl::Extractor ex (abstract_path.c_str ());
#if defined(HAVE_QT)
if (ex.test (":")) {
return abstract_path;
} else
#endif
#if defined(HAVE_CURL) || defined(HAVE_QT)
if (ex.test ("http:") || ex.test ("https:")) {
return abstract_path;
} else
#endif
if (ex.test ("pipe:")) {
} else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) {
return abstract_path;
} else if (ex.test ("file:")) {
tl::URI uri (abstract_path);
return tl::absolute_path (uri.path ());
return tl::absolute_file_path (uri.path ());
} else {
return tl::absolute_file_path (abstract_path);
}
}
const char *
bool InputStream::is_absolute (const std::string &abstract_path)
{
// TODO: align this implementation with InputStream ctor
tl::Extractor ex (abstract_path.c_str ());
if (ex.test (":")) {
return true;
} else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) {
return true;
} else if (ex.test ("file:")) {
tl::URI uri (abstract_path);
return tl::is_absolute (uri.path ());
} else {
return tl::is_absolute (abstract_path);
}
}
bool InputStream::is_file_path (const std::string &abstract_path)
{
tl::Extractor ex (abstract_path.c_str ());
if (ex.test (":")) {
return false;
} else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) {
return false;
} else {
return true;
}
}
std::string InputStream::as_file_path (const std::string &abstract_path)
{
tl::Extractor ex (abstract_path.c_str ());
if (ex.test (":")) {
return std::string ();
} else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) {
return std::string ();
} else if (ex.test ("file:")) {
tl::URI uri (abstract_path);
return uri.path ();
} else {
return abstract_path;
}
}
std::string InputStream::combine (const std::string &path1, const std::string &path2)
{
if (is_absolute (path2)) {
return path2;
}
tl::Extractor ex (path1);
if (ex.test (":")) {
return path1 + "/" + path2;
} else if (ex.test ("pipe:") || ex.test ("data:")) {
// ignore un-combinable first parts
return path2;
}
tl::URI uri1 (path1);
tl::URI uri2 (path2);
if (uri1.scheme ().empty ()) {
if (uri2.scheme ().empty ()) {
return tl::combine_path (path1, path2);
} else {
return tl::combine_path (path1, uri2.path ());
}
} else {
if (uri2.scheme ().empty ()) {
uri1.set_path (uri1.path () + "/" + tl::replaced (path2, "\\", "/"));
} else {
uri1.set_path (uri1.path () + "/" + uri2.path ());
}
return uri1.to_abstract_path ();
}
}
std::string InputStream::relative_path (const std::string &path1, const std::string &path2)
{
// TODO: align this implementation with InputStream ctor
tl::Extractor ex (path2);
if (ex.test (":")) {
return path2;
} else if (ex.test ("pipe:") || ex.test ("data:")) {
return path2;
}
tl::URI uri1 (path1);
tl::URI uri2 (path2);
// NOTE: only file schemes are supported as of now
if ((uri1.scheme ().empty () || uri1.scheme () == "file") &&
(uri2.scheme ().empty () || uri2.scheme () == "file")) {
return tl::relative_path (uri1.path (), uri2.path ());
}
return path2;
}
const char *
InputStream::get (size_t n, bool bypass_inflate)
{
// if deflating, employ the deflate filter to get the data

View File

@ -531,7 +531,7 @@ public:
*
* Returns an empty string if no absolute path is available.
*/
std::string absolute_path () const
std::string absolute_file_path () const
{
return mp_delegate->absolute_path ();
}
@ -549,9 +549,34 @@ public:
void close ();
/**
* @brief Gets the absolute path for a given URL
* @brief Gets the absolute, abstract path for a given abstract path
*/
static std::string absolute_path (const std::string &path);
static std::string absolute_file_path (const std::string &apath);
/**
* @brief Gets the absolute path for a given abstract path
*/
static bool is_absolute (const std::string &apath);
/**
* @brief Gets a value indicating whether the path is a file path
*/
static bool is_file_path (const std::string &apath);
/**
* @brief Gets the file path (no scheme) if it applies to the given abstract path
*/
static std::string as_file_path (const std::string &apath);
/**
* @brief Combines two abstract paths
*/
static std::string combine (const std::string &apath1, const std::string &apath2);
/**
* @brief Returns the relative abstract path of path2 vs. path1
*/
static std::string relative_path (const std::string &apath1, const std::string &apath2);
/**
* @brief Gets the base reader (delegate)
@ -621,7 +646,7 @@ public:
virtual std::string absolute_path () const
{
return m_inflating_stream.absolute_path ();
return m_inflating_stream.absolute_file_path ();
}
virtual std::string filename () const

View File

@ -479,3 +479,112 @@ TEST(RefuseToWrite)
}
}
TEST(AbstractPathFunctions)
{
EXPECT_EQ (tl::InputStream::absolute_file_path (""), tl::absolute_file_path ("."));
EXPECT_EQ (tl::InputStream::absolute_file_path ("."), tl::absolute_file_path ("."));
EXPECT_EQ (tl::InputStream::absolute_file_path ("pipe:xyz"), "pipe:xyz");
EXPECT_EQ (tl::InputStream::absolute_file_path ("data:xyz"), "data:xyz");
EXPECT_EQ (tl::InputStream::absolute_file_path ("https:xyz"), "https:xyz");
EXPECT_EQ (tl::InputStream::absolute_file_path ("http:xyz"), "http:xyz");
EXPECT_EQ (tl::InputStream::absolute_file_path (":xyz"), ":xyz");
EXPECT_EQ (tl::InputStream::absolute_file_path ("file:xyz"), tl::absolute_file_path ("xyz"));
EXPECT_EQ (tl::InputStream::absolute_file_path ("xyz"), tl::absolute_file_path ("xyz"));
EXPECT_EQ (tl::InputStream::absolute_file_path ("xyz/uvw"), tl::absolute_file_path ("xyz/uvw"));
EXPECT_EQ (tl::InputStream::absolute_file_path ("/xyz/uvw"), tl::absolute_file_path ("/xyz/uvw"));
tl::file_utils_force_windows ();
EXPECT_EQ (tl::InputStream::absolute_file_path ("xyz\\uvw"), tl::absolute_file_path ("xyz\\uvw"));
EXPECT_EQ (tl::InputStream::absolute_file_path ("\\\\server\\xyz\\uvw"), "\\\\server\\xyz\\uvw");
EXPECT_EQ (tl::InputStream::absolute_file_path ("C:\\xyz\\uvw"), "C:\\xyz\\uvw");
tl::file_utils_force_reset ();
EXPECT_EQ (tl::InputStream::is_absolute (""), false);
EXPECT_EQ (tl::InputStream::is_absolute ("."), false);
EXPECT_EQ (tl::InputStream::is_absolute ("pipe:xyz"), true);
EXPECT_EQ (tl::InputStream::is_absolute ("data:xyz"), true);
EXPECT_EQ (tl::InputStream::is_absolute ("https:xyz"), true);
EXPECT_EQ (tl::InputStream::is_absolute ("http:xyz"), true);
EXPECT_EQ (tl::InputStream::is_absolute (":xyz"), true);
EXPECT_EQ (tl::InputStream::is_absolute ("file:xyz"), false);
EXPECT_EQ (tl::InputStream::is_absolute ("xyz"), false);
EXPECT_EQ (tl::InputStream::is_absolute ("xyz/uvw"), false);
tl::file_utils_force_linux ();
EXPECT_EQ (tl::InputStream::is_absolute ("/xyz/uvw"), true);
tl::file_utils_force_windows ();
EXPECT_EQ (tl::InputStream::is_absolute ("xyz\\uvw"), false);
EXPECT_EQ (tl::InputStream::is_absolute ("\\\\server\\xyz\\uvw"), true);
EXPECT_EQ (tl::InputStream::is_absolute ("c:\\xyz\\uvw"), true);
tl::file_utils_force_reset ();
tl::file_utils_force_windows ();
EXPECT_EQ (tl::InputStream::combine ("a", ""), "a");
EXPECT_EQ (tl::InputStream::combine ("", "b"), "\\b");
EXPECT_EQ (tl::InputStream::combine ("a", "b"), "a\\b");
EXPECT_EQ (tl::InputStream::combine ("a", "b/c"), "a\\b/c");
EXPECT_EQ (tl::InputStream::combine ("a", "b\\c"), "a\\b\\c");
EXPECT_EQ (tl::InputStream::combine ("a", "data:abc"), "data:abc");
EXPECT_EQ (tl::InputStream::combine ("data:a", "b"), "b");
EXPECT_EQ (tl::InputStream::combine ("pipe:a", "b"), "b");
EXPECT_EQ (tl::InputStream::combine (":a", "b"), ":a/b");
EXPECT_EQ (tl::InputStream::combine ("https://a", "b"), "https://a/b");
EXPECT_EQ (tl::InputStream::combine ("https://a", "https:b"), "https:b");
EXPECT_EQ (tl::InputStream::combine ("a", "https:b"), "https:b");
EXPECT_EQ (tl::InputStream::combine ("a", "file:b"), "a\\b");
EXPECT_EQ (tl::InputStream::combine ("a", "file:\\b"), "file:\\b");
EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b"), "file:a/b");
EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b/c"), "file:a/b/c");
EXPECT_EQ (tl::InputStream::combine ("file:a", "b\\c"), "file:a/b/c");
tl::file_utils_force_linux ();
EXPECT_EQ (tl::InputStream::combine ("a", "b"), "a/b");
EXPECT_EQ (tl::InputStream::combine ("", "b"), "/b");
EXPECT_EQ (tl::InputStream::combine ("a", "b/c"), "a/b/c");
EXPECT_EQ (tl::InputStream::combine ("a", "data:abc"), "data:abc");
EXPECT_EQ (tl::InputStream::combine ("data:a", "b"), "b");
EXPECT_EQ (tl::InputStream::combine ("pipe:a", "b"), "b");
EXPECT_EQ (tl::InputStream::combine (":a", "b"), ":a/b");
EXPECT_EQ (tl::InputStream::combine ("https://a", "b"), "https://a/b");
EXPECT_EQ (tl::InputStream::combine ("https://a", "https:b"), "https:b");
EXPECT_EQ (tl::InputStream::combine ("a", "https:b"), "https:b");
EXPECT_EQ (tl::InputStream::combine ("a", "file:b"), "a/b");
EXPECT_EQ (tl::InputStream::combine ("a", "file:/b"), "file:/b");
EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b"), "file:a/b");
EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b/c"), "file:a/b/c");
EXPECT_EQ (tl::InputStream::combine ("file:a", "b/c"), "file:a/b/c");
tl::file_utils_force_reset ();
tl::file_utils_force_linux ();
EXPECT_EQ (tl::InputStream::relative_path ("", "file:/a/b/c"), "/a/b/c");
EXPECT_EQ (tl::InputStream::relative_path (".", "file:/a/b/c"), "/a/b/c");
EXPECT_EQ (tl::InputStream::relative_path ("https://x", "a/b/c"), "a/b/c");
EXPECT_EQ (tl::InputStream::relative_path ("file:/a/b", "file:/a/b/c"), "c");
EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/a/b/c"), "c");
EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/x/b/c"), "/x/b/c");
EXPECT_EQ (tl::InputStream::relative_path ("file:/a/b", "file:/a/b/c"), "c");
EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/a/b/c"), "c");
tl::file_utils_force_windows ();
EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/a/b/c"), "c");
EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "\\a\\b\\c\\d"), "c\\d");
tl::file_utils_force_reset ();
EXPECT_EQ (tl::InputStream::is_file_path (""), true);
EXPECT_EQ (tl::InputStream::is_file_path (":abc"), false);
EXPECT_EQ (tl::InputStream::is_file_path ("pipe:abc"), false);
EXPECT_EQ (tl::InputStream::is_file_path ("data:abc"), false);
EXPECT_EQ (tl::InputStream::is_file_path ("http:abc"), false);
EXPECT_EQ (tl::InputStream::is_file_path ("file:abc"), true);
EXPECT_EQ (tl::InputStream::is_file_path ("a/b/c"), true);
tl::file_utils_force_windows ();
EXPECT_EQ (tl::InputStream::is_file_path ("a\\b\\c"), true);
tl::file_utils_force_reset ();
EXPECT_EQ (tl::InputStream::as_file_path (""), std::string ());
EXPECT_EQ (tl::InputStream::as_file_path (":abc"), std::string ());
EXPECT_EQ (tl::InputStream::as_file_path ("pipe:abc"), std::string ());
EXPECT_EQ (tl::InputStream::as_file_path ("data:abc"), std::string ());
EXPECT_EQ (tl::InputStream::as_file_path ("http:abc"), std::string ());
EXPECT_EQ (tl::InputStream::as_file_path ("file:abc"), "abc");
EXPECT_EQ (tl::InputStream::as_file_path ("a/b/c"), "a/b/c");
tl::file_utils_force_windows ();
EXPECT_EQ (tl::InputStream::as_file_path ("a\\b\\c"), "a\\b\\c");
tl::file_utils_force_reset ();
}