diff --git a/src/lay/layMainWindow.cc b/src/lay/layMainWindow.cc index 33b57861e..809e90fd5 100644 --- a/src/lay/layMainWindow.cc +++ b/src/lay/layMainWindow.cc @@ -581,16 +581,6 @@ MainWindow::MainWindow (QApplication *app, const char *name) cp_frame_ly->insertSpacing (-1, 6); // connect to the menus to provide the dynamic parts - QMenu *edit_menu = mp_menu->menu ("edit_menu"); - tl_assert (edit_menu != 0); - connect (edit_menu, SIGNAL (aboutToShow ()), this, SLOT (popup_menu_show ())); - connect (edit_menu, SIGNAL (aboutToHide ()), this, SLOT (popup_menu_hide ())); - - QMenu *zoom_menu = mp_menu->menu ("zoom_menu"); - tl_assert (zoom_menu != 0); - connect (zoom_menu, SIGNAL (aboutToShow ()), this, SLOT (popup_menu_show ())); - connect (zoom_menu, SIGNAL (aboutToHide ()), this, SLOT (popup_menu_hide ())); - QMenu *bookmark_menu = mp_menu->menu ("bookmark_menu"); tl_assert (bookmark_menu != 0); connect (bookmark_menu, SIGNAL (aboutToShow ()), this, SLOT (bookmark_menu_show ())); @@ -673,6 +663,11 @@ MainWindow::MainWindow (QApplication *app, const char *name) connect (&m_file_changed_timer, SIGNAL (timeout ()), this, SLOT (file_changed_timer())); m_file_changed_timer.setSingleShot (true); + // install timer for menu update + connect (&m_menu_update_timer, SIGNAL (timeout ()), this, SLOT (update_action_states ())); + m_menu_update_timer.setSingleShot (false); + m_menu_update_timer.start (200); + connect (&lay::LayoutHandle::file_watcher (), SIGNAL (fileChanged (const QString &)), this, SLOT (file_changed (const QString &))); connect (&lay::LayoutHandle::file_watcher (), SIGNAL (fileRemoved (const QString &)), this, SLOT (file_removed (const QString &))); @@ -1695,9 +1690,6 @@ MainWindow::edits_enabled_changed () for (std::vector::const_iterator g = edit_grp.begin (); g != edit_grp.end (); ++g) { mp_menu->action (*g).set_enabled (enable); } - - // call this to establish a consistent set of options on the menu entries - popup_menu_show (); } void @@ -2664,123 +2656,73 @@ MainWindow::cm_zoom_out () } void -MainWindow::popup_menu_hide () +MainWindow::update_action_states () { - // Enable all menu items as far as required on closing of the menu. Otherwise, - // keyboard shortcuts are not effective when the window is closed and the actions are - // left disabled. + try { - BEGIN_PROTECTED + if (mp_menu->is_valid ("edit_menu.undo")) { - if (mp_menu->is_valid ("edit_menu.undo")) { - Action undo_action = mp_menu->action ("edit_menu.undo"); - undo_action.set_enabled (edits_enabled ()); - } + Action undo_action = mp_menu->action ("edit_menu.undo"); - if (mp_menu->is_valid ("edit_menu.redo")) { - Action redo_action = mp_menu->action ("edit_menu.redo"); - redo_action.set_enabled (edits_enabled ()); - } + std::string undo_txt (tl::to_string (QObject::tr ("&Undo"))); + bool undo_enable = false; + if (current_view () && m_manager.available_undo ().first) { + undo_txt += " - " + m_manager.available_undo ().second; + undo_enable = true; + } + undo_action.set_title (undo_txt); + undo_action.set_enabled (undo_enable && edits_enabled ()); - if (mp_menu->is_valid ("edit_menu.copy")) { - Action copy_action = mp_menu->action ("edit_menu.copy"); - copy_action.set_enabled (edits_enabled ()); - } - - if (mp_menu->is_valid ("edit_menu.cut")) { - Action cut_action = mp_menu->action ("edit_menu.cut"); - cut_action.set_enabled (edits_enabled ()); - } - - if (mp_menu->is_valid ("edit_menu.paste")) { - Action paste_action = mp_menu->action ("edit_menu.paste"); - paste_action.set_enabled (edits_enabled ()); - } - - if (mp_menu->is_valid ("edit_menu.duplicate")) { - Action paste_action = mp_menu->action ("edit_menu.duplicate"); - paste_action.set_enabled (edits_enabled ()); - } - - if (mp_menu->is_valid ("zoom_menu.next_display_state")) { - Action next_display_state_action = mp_menu->action ("zoom_menu.next_display_state"); - next_display_state_action.set_enabled (true); - } - - if (mp_menu->is_valid ("zoom_menu.last_display_state")) { - Action last_display_state_action = mp_menu->action ("zoom_menu.last_display_state"); - last_display_state_action.set_enabled (true); - } - - END_PROTECTED -} - -void -MainWindow::popup_menu_show () -{ - BEGIN_PROTECTED - - if (mp_menu->is_valid ("edit_menu.undo")) { - - Action undo_action = mp_menu->action ("edit_menu.undo"); - - std::string undo_txt (tl::to_string (QObject::tr ("&Undo"))); - bool undo_enable = false; - if (current_view () && m_manager.available_undo ().first) { - undo_txt += " - " + m_manager.available_undo ().second; - undo_enable = true; } - undo_action.set_title (undo_txt); - undo_action.set_enabled (undo_enable && edits_enabled ()); - } + if (mp_menu->is_valid ("edit_menu.redo")) { - if (mp_menu->is_valid ("edit_menu.redo")) { + Action redo_action = mp_menu->action ("edit_menu.redo"); - Action redo_action = mp_menu->action ("edit_menu.redo"); + std::string redo_txt (tl::to_string (QObject::tr ("&Redo"))); + bool redo_enable = false; + if (current_view () && m_manager.available_redo ().first) { + redo_txt += " - " + m_manager.available_redo ().second; + redo_enable = true; + } + redo_action.set_title (redo_txt); + redo_action.set_enabled (redo_enable && edits_enabled ()); - std::string redo_txt (tl::to_string (QObject::tr ("&Redo"))); - bool redo_enable = false; - if (current_view () && m_manager.available_redo ().first) { - redo_txt += " - " + m_manager.available_redo ().second; - redo_enable = true; } - redo_action.set_title (redo_txt); - redo_action.set_enabled (redo_enable && edits_enabled ()); + if (mp_menu->is_valid ("edit_menu.copy")) { + Action copy_action = mp_menu->action ("edit_menu.copy"); + copy_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ()); + } + + if (mp_menu->is_valid ("edit_menu.duplicate")) { + Action copy_action = mp_menu->action ("edit_menu.duplicate"); + copy_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ()); + } + + if (mp_menu->is_valid ("edit_menu.cut")) { + Action cut_action = mp_menu->action ("edit_menu.cut"); + cut_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ()); + } + + if (mp_menu->is_valid ("edit_menu.paste")) { + Action paste_action = mp_menu->action ("edit_menu.paste"); + paste_action.set_enabled (! db::Clipboard::instance ().empty () && edits_enabled ()); + } + + if (mp_menu->is_valid ("zoom_menu.next_display_state")) { + Action next_display_state_action = mp_menu->action ("zoom_menu.next_display_state"); + next_display_state_action.set_enabled (has_next_display_state ()); + } + + if (mp_menu->is_valid ("zoom_menu.last_display_state")) { + Action last_display_state_action = mp_menu->action ("zoom_menu.last_display_state"); + last_display_state_action.set_enabled (has_last_display_state ()); + } + + } catch (...) { + // ignore exceptions } - - if (mp_menu->is_valid ("edit_menu.copy")) { - Action copy_action = mp_menu->action ("edit_menu.copy"); - copy_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ()); - } - - if (mp_menu->is_valid ("edit_menu.duplicate")) { - Action copy_action = mp_menu->action ("edit_menu.duplicate"); - copy_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ()); - } - - if (mp_menu->is_valid ("edit_menu.cut")) { - Action cut_action = mp_menu->action ("edit_menu.cut"); - cut_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ()); - } - - if (mp_menu->is_valid ("edit_menu.paste")) { - Action paste_action = mp_menu->action ("edit_menu.paste"); - paste_action.set_enabled (! db::Clipboard::instance ().empty () && edits_enabled ()); - } - - if (mp_menu->is_valid ("zoom_menu.next_display_state")) { - Action next_display_state_action = mp_menu->action ("zoom_menu.next_display_state"); - next_display_state_action.set_enabled (has_next_display_state ()); - } - - if (mp_menu->is_valid ("zoom_menu.last_display_state")) { - Action last_display_state_action = mp_menu->action ("zoom_menu.last_display_state"); - last_display_state_action.set_enabled (has_last_display_state ()); - } - - END_PROTECTED } void diff --git a/src/lay/layMainWindow.h b/src/lay/layMainWindow.h index 7ad07616e..96331cc95 100644 --- a/src/lay/layMainWindow.h +++ b/src/lay/layMainWindow.h @@ -662,8 +662,7 @@ public slots: */ void intrinsic_mode_triggered (); - void popup_menu_show (); - void popup_menu_hide (); + void update_action_states (); void cancel (); void redraw (); void exit (); @@ -900,6 +899,7 @@ private: tl::DeferredMethod dm_do_update_menu; QTimer m_message_timer; QTimer m_file_changed_timer; + QTimer m_menu_update_timer; std::string m_config_window_state; std::string m_initial_technology; diff --git a/src/laybasic/layCellTreeModel.cc b/src/laybasic/layCellTreeModel.cc index 6cb7e928a..3c85fcbbf 100644 --- a/src/laybasic/layCellTreeModel.cc +++ b/src/laybasic/layCellTreeModel.cc @@ -756,21 +756,6 @@ CellTreeModel::search_children (const tl::GlobPattern &pattern, CellTreeItem *it } } -void -CellTreeModel::search_children (const char *name, CellTreeItem *item) -{ - int children = item->children (); - for (int i = 0; i < children; ++i) { - CellTreeItem *c = item->child (i); - if (c) { - if (c->name_equals (name)) { - m_selected_indexes.push_back (model_index (c)); - } - search_children (name, c); - } - } -} - QModelIndex CellTreeModel::locate (const char *name, bool glob_pattern, bool case_sensitive, bool top_only) { diff --git a/src/laybasic/layCellTreeModel.h b/src/laybasic/layCellTreeModel.h index 653d49f04..c5435588c 100644 --- a/src/laybasic/layCellTreeModel.h +++ b/src/laybasic/layCellTreeModel.h @@ -238,7 +238,6 @@ private: void build_top_level (); void clear_top_level (); void search_children (const tl::GlobPattern &pattern, CellTreeItem *item); - void search_children (const char *name, CellTreeItem *item); }; /** diff --git a/src/laybasic/layHierarchyControlPanel.cc b/src/laybasic/layHierarchyControlPanel.cc index ab8798605..3b11e1b2b 100644 --- a/src/laybasic/layHierarchyControlPanel.cc +++ b/src/laybasic/layHierarchyControlPanel.cc @@ -69,13 +69,9 @@ public: // -------------------------------------------------------------------- // HCPCellTreeWidget implementation -HCPCellTreeWidget::HCPCellTreeWidget (QWidget *parent, const char *name) - : QTreeView (parent) +HCPCellTreeWidget::HCPCellTreeWidget (QWidget *parent, const char *name, QWidget *key_event_receiver) + : QTreeView (parent), mp_key_event_receiver (key_event_receiver) { - // Don't request focus: this leaves focus on the canvas and the arrow keys functional there - // @@@ setFocusPolicy (Qt::NoFocus); -> solve differently!!! - setFocusPolicy (Qt::ClickFocus); - // Allow dragging from here to setDragDropMode (QAbstractItemView::DragOnly); @@ -97,12 +93,24 @@ HCPCellTreeWidget::event (QEvent *event) return QTreeView::event (event); } +bool +HCPCellTreeWidget::focusNextPrevChild (bool /*next*/) +{ + return false; +} + void HCPCellTreeWidget::keyPressEvent (QKeyEvent *event) { QString t = event->text (); - if (!t.isEmpty ()) { + if (!t.isEmpty () && t[0].isPrint ()) { emit search_triggered (t); + } else if (mp_key_event_receiver) { + // send other key events to the alternative receiver - this way we can make the + // view receive arrow keys for panning. + QCoreApplication::sendEvent (mp_key_event_receiver, event); + } else { + return QTreeView::keyPressEvent (event); } } @@ -885,7 +893,7 @@ HierarchyControlPanel::do_update_content (int cv_index) header->setVisible (split_mode); cl_ly->addWidget (header); - HCPCellTreeWidget *cell_list = new HCPCellTreeWidget (cl_frame, "tree"); + HCPCellTreeWidget *cell_list = new HCPCellTreeWidget (cl_frame, "tree", mp_view->view_object_widget ()); cl_ly->addWidget (cell_list); cell_list->setStyle (mp_tree_style.get ()); cell_list->setModel (new CellTreeModel (cell_list, mp_view, cv_index, m_flat ? CellTreeModel::Flat : 0, 0, m_sorting)); diff --git a/src/laybasic/layHierarchyControlPanel.h b/src/laybasic/layHierarchyControlPanel.h index 0a0618770..5bfb34ead 100644 --- a/src/laybasic/layHierarchyControlPanel.h +++ b/src/laybasic/layHierarchyControlPanel.h @@ -65,7 +65,7 @@ class HCPCellTreeWidget Q_OBJECT public: - HCPCellTreeWidget (QWidget *parent, const char *name); + HCPCellTreeWidget (QWidget *parent, const char *name, QWidget *key_event_receiver); signals: void cell_clicked (const QModelIndex &); @@ -78,8 +78,11 @@ protected: virtual void mousePressEvent (QMouseEvent *event); virtual void mouseReleaseEvent (QMouseEvent *event); virtual void startDrag (Qt::DropActions supportedActions); + virtual bool focusNextPrevChild (bool next); virtual void keyPressEvent (QKeyEvent *event); virtual bool event (QEvent *event); + + QWidget *mp_key_event_receiver; }; /** @@ -255,6 +258,14 @@ public: return m_split_mode; } + /** + * @brief Gets the layout view this panel is attached to + */ + lay::LayoutView *view () + { + return mp_view; + } + signals: void cell_selected (cell_path_type path, int cellview_index); void active_cellview_changed (int cellview_index); diff --git a/src/laybasic/layLayerControlPanel.cc b/src/laybasic/layLayerControlPanel.cc index c7d3df82d..0a8520e56 100644 --- a/src/laybasic/layLayerControlPanel.cc +++ b/src/laybasic/layLayerControlPanel.cc @@ -97,9 +97,6 @@ private: LCPTreeWidget::LCPTreeWidget (QWidget *parent, lay::LayerTreeModel *model, const char *name) : QTreeView (parent), mp_model (model) { - // Don't request focus: this leaves focus on the canvas and the arrow keys functional there - setFocusPolicy (Qt::NoFocus); - setObjectName (QString::fromUtf8 (name)); setModel (model); setItemDelegate (new LCPItemDelegate (this)); @@ -149,7 +146,38 @@ LCPTreeWidget::mouseDoubleClickEvent (QMouseEvent *event) } } -void +bool +LCPTreeWidget::focusNextPrevChild (bool /*next*/) +{ + return false; +} + +bool +LCPTreeWidget::event (QEvent *event) +{ + // Handling this event makes the widget receive all keystrokes + if (event->type () == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast (event); + QString t = ke->text (); + if (!t.isEmpty () && t[0].isPrint ()) { + ke->accept (); + } + } + return QTreeView::event (event); +} + +void +LCPTreeWidget::keyPressEvent (QKeyEvent *event) +{ + QString t = event->text (); + if (!t.isEmpty () && t[0].isPrint ()) { + emit search_triggered (t); + } else { + QTreeView::keyPressEvent (event); + } +} + +void LCPTreeWidget::collapse_all () { #if QT_VERSION >= 0x040200 @@ -299,6 +327,70 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage l->setMargin (0); l->setSpacing (0); + mp_search_frame = new QFrame (this); + l->addWidget (mp_search_frame); + mp_search_frame->hide (); + mp_search_frame->setAutoFillBackground (true); + mp_search_frame->setObjectName (QString::fromUtf8 ("panel")); + mp_search_frame->setFrameStyle (QFrame::Panel | QFrame::Raised); + mp_search_frame->setLineWidth (1); + mp_search_frame->setBackgroundRole (QPalette::Highlight); + + QHBoxLayout *sf_ly = new QHBoxLayout (mp_search_frame); + sf_ly->setMargin (0); + sf_ly->setContentsMargins (0, 0, 0, 0); + sf_ly->setSpacing (0); + + mp_search_close_cb = new QCheckBox (mp_search_frame); + sf_ly->addWidget (mp_search_close_cb); + + mp_search_close_cb->setFocusPolicy (Qt::NoFocus); + mp_search_close_cb->setBackgroundRole (QPalette::Highlight); + mp_search_close_cb->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred)); + QPalette pl (mp_search_close_cb->palette ()); + pl.setColor (QPalette::Foreground, pl.color (QPalette::Active, QPalette::HighlightedText)); + mp_search_close_cb->setPalette (pl); + mp_search_close_cb->setMaximumSize (QSize (mp_search_close_cb->maximumSize ().width (), mp_search_close_cb->sizeHint ().height () - 4)); + connect (mp_search_close_cb, SIGNAL (clicked ()), this, SLOT (search_editing_finished ())); + + mp_search_edit_box = new lay::DecoratedLineEdit (mp_search_frame); + mp_search_edit_box->setObjectName (QString::fromUtf8 ("cellview_search_edit_box")); + mp_search_edit_box->set_escape_signal_enabled (true); + mp_search_edit_box->set_tab_signal_enabled (true); + connect (mp_search_edit_box, SIGNAL (returnPressed ()), this, SLOT (search_editing_finished ())); + connect (mp_search_edit_box, SIGNAL (textEdited (const QString &)), this, SLOT (search_edited ())); + connect (mp_search_edit_box, SIGNAL (esc_pressed ()), this, SLOT (search_editing_finished ())); + connect (mp_search_edit_box, SIGNAL (tab_pressed ()), this, SLOT (search_next ())); + connect (mp_search_edit_box, SIGNAL (backtab_pressed ()), this, SLOT (search_prev ())); + sf_ly->addWidget (mp_search_edit_box); + + mp_use_regular_expressions = new QAction (this); + mp_use_regular_expressions->setCheckable (true); + mp_use_regular_expressions->setChecked (true); + mp_use_regular_expressions->setText (tr ("Use expressions (use * and ? for any character)")); + + mp_case_sensitive = new QAction (this); + mp_case_sensitive->setCheckable (true); + mp_case_sensitive->setChecked (true); + mp_case_sensitive->setText (tr ("Case sensitive search")); + + QMenu *m = new QMenu (mp_search_edit_box); + m->addAction (mp_use_regular_expressions); + m->addAction (mp_case_sensitive); + connect (mp_use_regular_expressions, SIGNAL (triggered ()), this, SLOT (search_edited ())); + connect (mp_case_sensitive, SIGNAL (triggered ()), this, SLOT (search_edited ())); + + mp_search_edit_box->set_clear_button_enabled (true); + mp_search_edit_box->set_options_button_enabled (true); + mp_search_edit_box->set_options_menu (m); + + QToolButton *sf_next = new QToolButton (mp_search_frame); + sf_next->setAutoRaise (true); + sf_next->setToolTip (tr ("Find next")); + sf_next->setIcon (QIcon (QString::fromUtf8 (":/find.png"))); + connect (sf_next, SIGNAL (clicked ()), this, SLOT (search_next ())); + sf_ly->addWidget (sf_next); + mp_tab_bar = new QTabBar (this); mp_tab_bar->setObjectName (QString::fromUtf8 ("lcp_tabs")); connect (mp_tab_bar, SIGNAL (currentChanged (int)), this, SLOT (tab_selected (int))); @@ -321,6 +413,7 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage connect (mp_layer_list, SIGNAL (double_clicked (const QModelIndex &, Qt::KeyboardModifiers)), this, SLOT (double_clicked (const QModelIndex &, Qt::KeyboardModifiers))); connect (mp_layer_list, SIGNAL (collapsed (const QModelIndex &)), this, SLOT (group_collapsed (const QModelIndex &))); connect (mp_layer_list, SIGNAL (expanded (const QModelIndex &)), this, SLOT (group_expanded (const QModelIndex &))); + connect (mp_layer_list, SIGNAL (search_triggered (const QString &)), this, SLOT (search_triggered (const QString &))); mp_layer_list->setContextMenuPolicy (Qt::CustomContextMenu); connect (mp_layer_list, SIGNAL(customContextMenuRequested (const QPoint &)), this, SLOT (context_menu (const QPoint &))); mp_layer_list->header ()->hide (); @@ -1130,6 +1223,77 @@ LayerControlPanel::clear_selection () set_selection (empty_sel); } +void +LayerControlPanel::search_triggered (const QString &t) +{ + if (mp_model) { + mp_search_close_cb->setChecked (true); + mp_search_frame->show (); + mp_search_edit_box->setText (t); + mp_search_edit_box->setFocus (); + search_edited (); + } +} + +void +LayerControlPanel::search_edited () +{ + if (! mp_model) { + return; + } + + QString t = mp_search_edit_box->text (); + if (t.isEmpty ()) { + mp_model->clear_locate (); + mp_layer_list->setCurrentIndex (QModelIndex ()); + } else { + QModelIndex found = mp_model->locate (t.toUtf8 ().constData (), mp_use_regular_expressions->isChecked (), mp_case_sensitive->isChecked (), false); + mp_layer_list->setCurrentIndex (found); + if (found.isValid ()) { + mp_layer_list->scrollTo (found); + } + } +} + +void +LayerControlPanel::search_next () +{ + if (! mp_model) { + return; + } + + QModelIndex found = mp_model->locate_next (); + if (found.isValid ()) { + mp_layer_list->setCurrentIndex (found); + mp_layer_list->scrollTo (found); + } +} + +void +LayerControlPanel::search_prev () +{ + if (! mp_model) { + return; + } + + QModelIndex found = mp_model->locate_prev (); + if (found.isValid ()) { + mp_layer_list->setCurrentIndex (found); + mp_layer_list->scrollTo (found); + } +} + +void +LayerControlPanel::search_editing_finished () +{ + if (! mp_model) { + return; + } + + mp_model->clear_locate (); + mp_search_frame->hide (); +} + void LayerControlPanel::cm_regroup_flatten () { @@ -1727,6 +1891,10 @@ set_hidden_flags_rec (LayerTreeModel *model, QTreeView *tree_view, const QModelI void LayerControlPanel::do_update_content () { + // clear search. TODO: update search instead of clearing + mp_search_edit_box->clear (); + mp_model->clear_locate(); + mp_model->set_phase (m_phase); if (m_tabs_need_update) { diff --git a/src/laybasic/layLayerControlPanel.h b/src/laybasic/layLayerControlPanel.h index a99af4932..4b230257f 100644 --- a/src/laybasic/layLayerControlPanel.h +++ b/src/laybasic/layLayerControlPanel.h @@ -44,6 +44,7 @@ #include "layDitherPattern.h" #include "layLayerProperties.h" #include "layLayerTreeModel.h" +#include "layWidgets.h" #include "dbObject.h" #include "tlDeferredExecution.h" @@ -52,6 +53,7 @@ class QModelIndex; class QMenu; class QLabel; class QTabBar; +class QCheckBox; namespace lay { @@ -85,6 +87,12 @@ public: signals: void double_clicked (const QModelIndex &, Qt::KeyboardModifiers); + void search_triggered (const QString &t); + +protected: + virtual void keyPressEvent (QKeyEvent *event); + virtual bool event (QEvent *event); + virtual bool focusNextPrevChild (bool next); private: lay::LayerTreeModel *mp_model; @@ -322,6 +330,11 @@ public slots: void upup_clicked (); void down_clicked (); void downdown_clicked (); + void search_triggered (const QString &t); + void search_edited (); + void search_editing_finished (); + void search_next (); + void search_prev (); private: QTabBar *mp_tab_bar; @@ -341,6 +354,11 @@ private: std::set m_expanded; bool m_no_stipples; QLabel *m_no_stipples_label; + lay::DecoratedLineEdit *mp_search_edit_box; + QAction *mp_case_sensitive; + QAction *mp_use_regular_expressions; + QFrame *mp_search_frame; + QCheckBox *mp_search_close_cb; void clear_selection (); diff --git a/src/laybasic/layLayerTreeModel.cc b/src/laybasic/layLayerTreeModel.cc index 0ce7b1a93..05af88be6 100644 --- a/src/laybasic/layLayerTreeModel.cc +++ b/src/laybasic/layLayerTreeModel.cc @@ -27,6 +27,7 @@ #include "dbLayoutUtils.h" #include "tlLog.h" #include "tlTimer.h" +#include "tlGlobPattern.h" #include #include @@ -278,6 +279,92 @@ LayerTreeModel::index (int row, int column, const QModelIndex &parent) const } } +void +LayerTreeModel::clear_locate () +{ + m_selected_indexes.clear (); + m_current_index = m_selected_indexes.begin (); + m_selected_ids.clear (); + + signal_data_changed (); +} + +QModelIndex +LayerTreeModel::locate_next () +{ + if (m_current_index == m_selected_indexes.end ()) { + return QModelIndex (); + } else { + ++m_current_index; + if (m_current_index == m_selected_indexes.end ()) { + m_current_index = m_selected_indexes.begin (); + } + return *m_current_index; + } +} + +QModelIndex +LayerTreeModel::locate_prev () +{ + if (m_current_index == m_selected_indexes.end ()) { + return QModelIndex (); + } else { + if (m_current_index == m_selected_indexes.begin ()) { + m_current_index = m_selected_indexes.end (); + } + --m_current_index; + return *m_current_index; + } +} + +void +LayerTreeModel::search_children (const tl::GlobPattern &pattern, const QModelIndex &parent, bool recurse) +{ + int children = rowCount (parent); + for (int i = 0; i < children; ++i) { + + QModelIndex child = index (i, 0, parent); + + lay::LayerPropertiesConstIterator iter (iterator (child)); + if (!iter.is_null () && !iter.at_end () && + pattern.match (iter->display_string (mp_view, true /*real*/))) { + m_selected_indexes.push_back (child); + } + + if (recurse && iter->has_children ()) { + search_children (pattern, child, recurse); + } + + } +} + +QModelIndex +LayerTreeModel::locate (const char *name, bool glob_pattern, bool case_sensitive, bool top_only) +{ + m_selected_indexes.clear (); + + tl::GlobPattern p = tl::GlobPattern (std::string (name)); + p.set_case_sensitive (case_sensitive); + p.set_exact (!glob_pattern); + p.set_header_match (true); + + search_children (p, QModelIndex (), !top_only); + + m_selected_ids.clear (); + for (std::vector::const_iterator i = m_selected_indexes.begin (); i != m_selected_indexes.end (); ++i) { + m_selected_ids.insert (size_t (i->internalPointer ())); + } + + signal_data_changed (); + + m_current_index = m_selected_indexes.begin (); + if (m_current_index == m_selected_indexes.end ()) { + return QModelIndex (); + } else { + return *m_current_index; + } +} + void LayerTreeModel::signal_data_changed () { @@ -648,6 +735,18 @@ LayerTreeModel::data (const QModelIndex &index, int role) const } + } else if (role == Qt::BackgroundRole) { + + if (m_selected_ids.find (size_t (index.internalPointer ())) != m_selected_ids.end ()) { + // for selected items pick a color between Highlight and Base + QPalette pl (mp_view->palette ()); + QColor c1 = pl.color (QPalette::Highlight); + QColor cb = pl.color (QPalette::Base); + return QVariant (QColor ((c1.red () + cb.red ()) / 2, (c1.green () + cb.green ()) / 2, (c1.blue () + cb.blue ()) / 2)); + } else { + return QVariant (); + } + } else if (role == Qt::TextColorRole || role == Qt::FontRole) { if (index.column () == 1) { diff --git a/src/laybasic/layLayerTreeModel.h b/src/laybasic/layLayerTreeModel.h index c130daf3d..d649dc406 100644 --- a/src/laybasic/layLayerTreeModel.h +++ b/src/laybasic/layLayerTreeModel.h @@ -38,6 +38,11 @@ namespace db class Layout; } +namespace tl +{ + class GlobPattern; +} + namespace lay { @@ -161,6 +166,29 @@ public: */ void signal_data_changed (); + /** + * @brief Locate an index by name (at least closest) + * + * If top_only is set, only top-level items are searched. An invalid model index is returned if + * no corresponding item could be found. + */ + QModelIndex locate (const char *name, bool glob_pattern = false, bool case_sensitive = true, bool top_only = true); + + /** + * @brief Locate the next index (after the first locate) + */ + QModelIndex locate_next (); + + /** + * @brief Locate the previous index (after the first locate) + */ + QModelIndex locate_prev (); + + /** + * @brief Clears the locate flags + */ + void clear_locate (); + /** * @brief Set the test_shapes_in_view flag * @@ -194,6 +222,11 @@ private: QFont m_font; QColor m_text_color, m_background_color; mutable EmptyWithinViewCache m_test_shapes_cache; + std::set m_selected_ids; + std::vector m_selected_indexes; + std::vector ::const_iterator m_current_index; + + void search_children (const tl::GlobPattern &pattern, const QModelIndex &parent, bool recurse); }; } diff --git a/src/laybasic/layLayoutView.cc b/src/laybasic/layLayoutView.cc index 8e83c2c86..cd909b10b 100644 --- a/src/laybasic/layLayoutView.cc +++ b/src/laybasic/layLayoutView.cc @@ -42,6 +42,7 @@ #include "tlString.h" #include "tlLog.h" #include "tlAssert.h" +#include "layLayoutView.h" #include "layViewOp.h" #include "layViewObject.h" #include "layLayoutViewConfigPages.h" diff --git a/src/laybasic/layViewObject.cc b/src/laybasic/layViewObject.cc index 6468a4250..972b708ca 100644 --- a/src/laybasic/layViewObject.cc +++ b/src/laybasic/layViewObject.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include "layViewObject.h" #include "layCanvasPlane.h" @@ -367,6 +368,12 @@ ViewObjectWidget::end_mouse_event () } } +bool +ViewObjectWidget::focusNextPrevChild (bool /*next*/) +{ + return false; +} + void ViewObjectWidget::keyPressEvent (QKeyEvent *e) { diff --git a/src/laybasic/layViewObject.h b/src/laybasic/layViewObject.h index e755b0307..4810f39e8 100644 --- a/src/laybasic/layViewObject.h +++ b/src/laybasic/layViewObject.h @@ -603,66 +603,6 @@ public: */ ~ViewObjectWidget (); - /** - * @brief Qt keyboard event handler - */ - void keyPressEvent (QKeyEvent *e); - - /** - * @brief Qt mouse move event handler - */ - void mouseMoveEvent (QMouseEvent *e); - - /** - * @brief Qt mouse leave event handler - */ - void leaveEvent (QEvent *e); - - /** - * @brief Qt mouse enter event handler - */ - void enterEvent (QEvent *e); - - /** - * @brief Qt mouse button press event handler - */ - void mousePressEvent (QMouseEvent *e); - - /** - * @brief Qt mouse button double-click event handler - */ - void mouseDoubleClickEvent (QMouseEvent *e); - - /** - * @brief Qt mouse button release event handler - */ - void mouseReleaseEvent (QMouseEvent *e); - - /** - * @brief Qt drag enter event handler - */ - void dragEnterEvent (QDragEnterEvent *event); - - /** - * @brief Qt drag leave event handler - */ - void dragLeaveEvent (QDragLeaveEvent *event); - - /** - * @brief Qt drag enter event handler - */ - void dragMoveEvent (QDragMoveEvent *event); - - /** - * @brief Qt drop event handler - */ - void dropEvent (QDropEvent *event); - - /** - * @brief Qt mouse wheel event handler - */ - void wheelEvent (QWheelEvent *e); - /** * @brief Cancel all drag operations */ @@ -966,6 +906,71 @@ public: void set_default_cursor (lay::Cursor::cursor_shape cursor); protected: + /** + * @brief Qt focus event handler + */ + bool focusNextPrevChild (bool next); + + /** + * @brief Qt keyboard event handler + */ + void keyPressEvent (QKeyEvent *e); + + /** + * @brief Qt mouse move event handler + */ + void mouseMoveEvent (QMouseEvent *e); + + /** + * @brief Qt mouse leave event handler + */ + void leaveEvent (QEvent *e); + + /** + * @brief Qt mouse enter event handler + */ + void enterEvent (QEvent *e); + + /** + * @brief Qt mouse button press event handler + */ + void mousePressEvent (QMouseEvent *e); + + /** + * @brief Qt mouse button double-click event handler + */ + void mouseDoubleClickEvent (QMouseEvent *e); + + /** + * @brief Qt mouse button release event handler + */ + void mouseReleaseEvent (QMouseEvent *e); + + /** + * @brief Qt drag enter event handler + */ + void dragEnterEvent (QDragEnterEvent *event); + + /** + * @brief Qt drag leave event handler + */ + void dragLeaveEvent (QDragLeaveEvent *event); + + /** + * @brief Qt drag enter event handler + */ + void dragMoveEvent (QDragMoveEvent *event); + + /** + * @brief Qt drop event handler + */ + void dropEvent (QDropEvent *event); + + /** + * @brief Qt mouse wheel event handler + */ + void wheelEvent (QWheelEvent *e); + /** * @brief Set the transformation for mouse events */ diff --git a/src/laybasic/layWidgets.cc b/src/laybasic/layWidgets.cc index deadd4923..7182692cc 100644 --- a/src/laybasic/layWidgets.cc +++ b/src/laybasic/layWidgets.cc @@ -976,6 +976,8 @@ bool DecoratedLineEdit::event (QEvent *event) QKeyEvent *ke = static_cast (event); if (ke->key () == Qt::Key_Escape && m_escape_signal_enabled) { ke->accept (); + } else if ((ke->key () == Qt::Key_Tab || ke->key () == Qt::Key_Backtab) && m_tab_signal_enabled) { + ke->accept (); } } return QLineEdit::event (event);