diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 7b7579f1d..5f17326a7 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -6487,6 +6487,19 @@ ref-edit = .add-to-working-set = إضافة إلى مجموعة العمل .remove-from-working-set = إزالة من مجموعة العمل .toolbar-objects = كائنات + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = حدد المرجع: .reference-not-found = لم يتم العثور على المرجع. .not-valid-reference = الكائن ليس مرجعًا صالحًا diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 7d027db91..a81c01159 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -6475,6 +6475,19 @@ ref-edit = .add-to-working-set = Добави към работния набор .remove-from-working-set = Премахни от работния набор .toolbar-objects = обекта + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Изберете препратка: .reference-not-found = Препратката не е намерена. .not-valid-reference = Обектът не е валидна препратка diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index e01075f44..992b21037 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -6472,6 +6472,19 @@ ref-edit = .add-to-working-set = Přidat do pracovní sady .remove-from-working-set = Odebrat z pracovní sady .toolbar-objects = objektů + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Vyberte referenci: .reference-not-found = Reference nebyla nalezena. .not-valid-reference = Objekt není platná reference diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index bffc99c55..ba4bdd85f 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -6473,6 +6473,19 @@ ref-edit = .add-to-working-set = Zum Arbeitssatz hinzufügen .remove-from-working-set = Aus dem Arbeitssatz entfernen .toolbar-objects = Objekte + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Referenz wählen: .reference-not-found = Referenz nicht gefunden. .not-valid-reference = Objekt ist keine gültige Referenz diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index 36d30766e..c5fcb2024 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -6475,6 +6475,19 @@ ref-edit = .add-to-working-set = Προσθήκη στο σύνολο εργασίας .remove-from-working-set = Αφαίρεση από το σύνολο εργασίας .toolbar-objects = αντικείμενα + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Επιλέξτε αναφορά: .reference-not-found = Η αναφορά δεν βρέθηκε. .not-valid-reference = Το αντικείμενο δεν είναι έγκυρη αναφορά diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 4a40e75d0..06224fbcd 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -6474,6 +6474,19 @@ ref-edit = .add-to-working-set = Add to Working Set .remove-from-working-set = Remove from Working Set .toolbar-objects = objects + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Select reference: .reference-not-found = Reference not found. .not-valid-reference = Object is not a valid reference diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 0b97e5beb..43c6c65d1 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -6475,6 +6475,19 @@ ref-edit = .add-to-working-set = Añadir al conjunto de trabajo .remove-from-working-set = Eliminar del conjunto de trabajo .toolbar-objects = objetos + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Seleccione la referencia: .reference-not-found = No se encuentra la referencia. .not-valid-reference = El objeto no es una referencia válida diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index ef3c2480e..301147008 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -6472,6 +6472,19 @@ ref-edit = .add-to-working-set = Lisää työjoukkoon .remove-from-working-set = Poista työjoukosta .toolbar-objects = objektia + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Valitse viittaus: .reference-not-found = Viittausta ei löydy. .not-valid-reference = Objekti ei ole kelvollinen viittaus diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 14569fb77..2132b6276 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -6475,6 +6475,19 @@ ref-edit = .add-to-working-set = Ajouter au jeu de travail .remove-from-working-set = Supprimer du jeu de travail .toolbar-objects = objets + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Sélectionnez la référence : .reference-not-found = Référence introuvable. .not-valid-reference = L'objet n'est pas une référence valide diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index cbd75e7da..b39b3f24f 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -6463,6 +6463,19 @@ ref-edit = .add-to-working-set = कार्य सेट में जोड़ें .remove-from-working-set = कार्य सेट से हटाएँ .toolbar-objects = ऑब्जेक्ट + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = संदर्भ चुनें: .reference-not-found = संदर्भ नहीं मिला। .not-valid-reference = ऑब्जेक्ट मान्य संदर्भ नहीं है diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index a7f885ac2..ccb348b8a 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -6459,6 +6459,19 @@ ref-edit = .add-to-working-set = Hozzáadás a munkakészlethez .remove-from-working-set = Eltávolítás a munkakészletből .toolbar-objects = objektum + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Válassza ki a hivatkozást: .reference-not-found = A hivatkozás nem található. .not-valid-reference = Az objektum nem érvényes hivatkozás diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 3cd2940d0..c16802be0 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -6474,6 +6474,19 @@ ref-edit = .add-to-working-set = Aggiungi al gruppo di lavoro .remove-from-working-set = Rimuovi dal gruppo di lavoro .toolbar-objects = oggetti + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Selezionare il riferimento: .reference-not-found = Riferimento non trovato. .not-valid-reference = L'oggetto non è un riferimento valido diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index d82902a93..01fb902b5 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -6463,6 +6463,19 @@ ref-edit = .add-to-working-set = 作業セットに追加 .remove-from-working-set = 作業セットから除去 .toolbar-objects = 個のオブジェクト + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = 参照を選択: .reference-not-found = 参照が見つかりません。 .not-valid-reference = オブジェクトは有効な参照ではありません diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 24a297190..e3a0fe994 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -6474,6 +6474,19 @@ ref-edit = .add-to-working-set = 작업 세트에 추가 .remove-from-working-set = 작업 세트에서 제거 .toolbar-objects = 개 객체 + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = 참조 선택: .reference-not-found = 참조를 찾을 수 없습니다. .not-valid-reference = 객체가 유효한 참조가 아닙니다 diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index 5fe731eed..81e7236d0 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -6475,6 +6475,19 @@ ref-edit = .add-to-working-set = ເພີ່ມໃສ່ຊຸດເຮັດວຽກ .remove-from-working-set = ລຶບອອກຈາກຊຸດເຮັດວຽກ .toolbar-objects = ວັດຖຸ + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = ເລືອກການອ້າງອີງ: .reference-not-found = ບໍ່ພົບການອ້າງອີງ. .not-valid-reference = ວັດຖຸບໍ່ແມ່ນການອ້າງອີງທີ່ຖືກຕ້ອງ diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index ac24c7998..fbc2136ac 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -6473,6 +6473,19 @@ ref-edit = .add-to-working-set = Toevoegen aan werkset .remove-from-working-set = Verwijderen uit werkset .toolbar-objects = objecten + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Selecteer referentie: .reference-not-found = Referentie niet gevonden. .not-valid-reference = Object is geen geldige referentie diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 961e0a3ff..77ba1001a 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -6474,6 +6474,19 @@ ref-edit = .add-to-working-set = Dodaj do zbioru roboczego .remove-from-working-set = Usuń ze zbioru roboczego .toolbar-objects = obiekty + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Wybierz odnośnik: .reference-not-found = Nie znaleziono odnośnika. .not-valid-reference = Obiekt nie jest prawidłowym odnośnikiem diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index 9c2f30281..6f2b255cd 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -6475,6 +6475,19 @@ ref-edit = .add-to-working-set = Adicionar ao conjunto de trabalho .remove-from-working-set = Remover do conjunto de trabalho .toolbar-objects = objetos + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Selecione a referência: .reference-not-found = Referência não encontrada. .not-valid-reference = O objeto não é uma referência válida diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 077b381d3..2ed996173 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -6476,6 +6476,19 @@ ref-edit = .add-to-working-set = Добавить в рабочий набор .remove-from-working-set = Удалить из рабочего набора .toolbar-objects = объектов + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = Выберите ссылку: .reference-not-found = Ссылка не найдена. .not-valid-reference = Объект не является допустимой ссылкой diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 1bd746587..ad21f3789 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -6396,6 +6396,19 @@ ref-edit = .add-to-working-set = Çalışma Kümesine Ekle .remove-from-working-set = Çalışma Kümesinden Çıkar .toolbar-objects = nesne + .dialog-title = Referansı Düzenle + .dialog-reference = Düzenlenecek referans + .dialog-chain-hint = Seçilen noktadaki zincir; bir çipe tıklayarak düzeyi değiştirin. + .dialog-path = Yol: + .dialog-working-set = Çalışma kümesi + .dialog-all-objects = Tüm nesneler + .dialog-pick-objects = Çizimde seçeceğim + .dialog-attributes = Öznitelik tanımlarını da düzenle + .dialog-session = Oturum + .dialog-lock = Kümede olmayan nesneleri kilitle + .dialog-unique = Çakışan katman, stil ve blok adlarını benzersiz yap + .dialog-start = Düzenlemeye Başla + .dialog-help = Bir blok referansını veya dış referansı yerinde düzenlemek için açar. .select-reference = Referans seçin: .reference-not-found = Referans bulunamadı. .not-valid-reference = Nesne geçerli bir referans değil diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 91da6cdbd..d07a7c4b9 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -6458,6 +6458,19 @@ ref-edit = .add-to-working-set = 添加到工作集 .remove-from-working-set = 从工作集删除 .toolbar-objects = 个对象 + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = 选择参照: .reference-not-found = 找不到参照。 .not-valid-reference = 对象不是有效的参照 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 03f63b634..2635b92cc 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -6470,6 +6470,19 @@ ref-edit = .add-to-working-set = 加入工作集 .remove-from-working-set = 從工作集移除 .toolbar-objects = 個物件 + .dialog-title = Reference Edit + .dialog-reference = Reference to edit + .dialog-chain-hint = The chain at the picked point; click a chip to change the level. + .dialog-path = Path: + .dialog-working-set = Working set + .dialog-all-objects = All objects + .dialog-pick-objects = Pick in the drawing + .dialog-attributes = Also edit attribute definitions + .dialog-session = Session + .dialog-lock = Lock objects not in the working set + .dialog-unique = Give clashing layer, style and block names unique names + .dialog-start = Start Editing + .dialog-help = Checks out a block reference or an external reference for editing in place. .select-reference = 選取參考: .reference-not-found = 找不到參考。 .not-valid-reference = 物件不是有效的參考 diff --git a/src/app/commands/refedit.rs b/src/app/commands/refedit.rs index b56d3edd5..3c924187b 100644 --- a/src/app/commands/refedit.rs +++ b/src/app/commands/refedit.rs @@ -4,10 +4,11 @@ use crate::app::{Message, OpenCADStudio}; use crate::modules::draw::modify::refedit::{ - apply_insert_inverse_transform, apply_insert_transform, level_hits, - nesting_chain, RefCloseCommand, RefEditOptionsCommand, RefEditPickCommand, RefEditSession, - RefSetCommand, NOT_CHECKED_OUT, + apply_insert_inverse_transform, apply_insert_transform, level_hits, nesting_chain, + rename_symbols, ChainLevel, RefCloseCommand, RefEditNames, RefEditOptionsCommand, + RefEditPickCommand, RefEditSession, RefSetCommand, SymbolKind, NOT_CHECKED_OUT, }; +use crate::ui::window::refedit_dialog::{RefEditDialogMsg, RefEditDialogState, RefEditLevel}; use codec::{EntityType, Handle}; use iced::Task; use std::collections::HashSet; @@ -147,25 +148,127 @@ impl OpenCADStudio { .iter() .map(|level| level_hits(&self.tabs[i].scene.document, level)) .collect(); - let command = RefEditOptionsCommand::new(top, chain, hits); + let command = RefEditOptionsCommand::new( + top, + chain, + hits, + self.refedit_options.lock, + self.refedit_options.unique_names, + ); use crate::command::CadCommand; self.command_line.push_info(&command.prompt()); self.tabs[i].active_cmd = Some(Box::new(command)); } else { - // The dialog's defaults: the innermost reference, all objects, - // attribute definitions not displayed. - let path: Vec = chain[1..] - .iter() - .map(|l| l.insert.value().to_string()) - .collect(); - let _ = self.dispatch_command(&format!( - "REFEDIT_BEGIN:{}:{}:0:", - top.value(), - path.join(",") - )); + self.open_refedit_dialog(i, top, chain); } } + /// The Edit Reference dialog over the picked chain: the innermost level + /// chosen, the remembered settings. + fn open_refedit_dialog(&mut self, i: usize, top: Handle, chain: Vec) { + let scene = &self.tabs[i].scene; + let document = &scene.document; + let levels = chain + .iter() + .map(|level| { + let record = document.block_records.get(&level.block_name); + RefEditLevel { + insert: level.insert, + name: level.block_name.clone(), + wires: scene.block_preview_wires(&level.block_name), + has_attdefs: record.is_some_and(|br| { + br.entity_handles.iter().any(|h| { + matches!(document.get_entity(*h), Some(EntityType::AttributeDefinition(_))) + }) + }), + xref_path: record + .filter(|br| br.flags.is_xref || br.flags.is_xref_overlay) + .map(|br| br.xref_path.clone()) + .filter(|p| !p.is_empty()), + } + }) + .collect::>(); + let options = &self.refedit_options; + self.refedit_dialog = Some(RefEditDialogState { + top, + level: levels.len().saturating_sub(1), + levels, + chain, + nested: options.nested, + attributes: options.attributes, + lock: options.lock, + unique: options.unique_names, + }); + self.active_modal = Some(crate::app::ModalKind::RefEdit); + } + + pub(in crate::app) fn update_refedit_dialog(&mut self, message: RefEditDialogMsg) -> Task { + let Some(state) = self.refedit_dialog.as_mut() else { + return Task::none(); + }; + match message { + RefEditDialogMsg::Level(level) => { + state.level = level.min(state.levels.len().saturating_sub(1)) + } + RefEditDialogMsg::Nested(on) => state.nested = on, + RefEditDialogMsg::Attributes(on) => state.attributes = on, + RefEditDialogMsg::Lock(on) => state.lock = on, + RefEditDialogMsg::Unique(on) => state.unique = on, + RefEditDialogMsg::Help => { + self.command_line + .push_info(&crate::tr!("ref-edit", "dialog-help")); + } + RefEditDialogMsg::Ok => { + let Some(state) = self.refedit_dialog.take() else { + return Task::none(); + }; + self.close_active_modal(); + let attributes = state.attributes && state.attributes_enabled(); + self.refedit_options = crate::app::settings::RefEditOptions { + unique_names: state.unique, + attributes: state.attributes, + lock: state.lock, + nested: state.nested, + }; + let i = self.active_tab; + if state.nested { + let hits = state + .chain + .iter() + .map(|level| level_hits(&self.tabs[i].scene.document, level)) + .collect(); + let command = RefEditOptionsCommand::nested_pick( + state.top, + state.chain, + hits, + state.level, + attributes, + state.lock, + state.unique, + ); + use crate::command::CadCommand; + self.command_line.push_info(&command.prompt()); + self.tabs[i].active_cmd = Some(Box::new(command)); + } else { + let last = state.level.min(state.chain.len().saturating_sub(1)); + let path: Vec = state.chain[1..=last] + .iter() + .map(|l| l.insert.value().to_string()) + .collect(); + return self.dispatch_command(&format!( + "REFEDIT_BEGIN:{}:{}:{}::{}:{}", + state.top.value(), + path.join(","), + attributes as u8, + state.lock as u8, + state.unique as u8 + )); + } + } + } + Task::none() + } + /// `top[:path[:attributes[:nested]]]` — open the session. fn begin_refedit(&mut self, i: usize, args: &str) { let mut parts = args.split(':'); @@ -186,6 +289,10 @@ impl OpenCADStudio { let path = handles(parts.next()); let attributes = parts.next() == Some("1"); let nested = handles(parts.next()); + let lock = parts.next().map_or(self.refedit_options.lock, |v| v == "1"); + let unique = parts + .next() + .map_or(self.refedit_options.unique_names, |v| v == "1"); if self.tabs[i].refedit_session.is_some() || self.reject_locked_edit(i, top) { return; } @@ -211,6 +318,15 @@ impl OpenCADStudio { }); } let level = chain.last().expect("non-empty chain").clone(); + // An external reference's dependent names (`XREF|NAME`) of the + // outermost xref in the chain. + let xref_prefix = chain.iter().find_map(|l| { + document + .block_records + .get(&l.block_name) + .filter(|br| br.flags.is_xref || br.flags.is_xref_overlay) + .map(|br| format!("{}|", br.name)) + }); let Some(br) = document.block_records.get(&level.block_name) else { return; }; @@ -245,6 +361,8 @@ impl OpenCADStudio { // Everything allocated from here on is part of the session. handle_watermark: document.next_handle().max(document.header.handle_seed), xref_path, + lock, + names: RefEditNames::default(), }; let copies: Vec = extracted .iter() @@ -252,6 +370,15 @@ impl OpenCADStudio { .collect(); self.push_undo_snapshot(i, "REFEDIT"); + let mut session = session; + let copies = match xref_prefix { + Some(prefix) => { + let (copies, names) = self.refedit_host_names(i, copies, &prefix, unique); + session.names = names; + copies + } + None => copies, + }; let mut temp_handles = Vec::new(); for mut entity in copies { apply_insert_transform(&mut entity, &session); @@ -259,7 +386,6 @@ impl OpenCADStudio { entity.common_mut().owner_handle = Handle::NULL; temp_handles.push(self.tabs[i].scene.add_entity(entity)); } - let mut session = session; session.temp_handles = temp_handles.clone(); self.tabs[i].refedit_session = Some(session); self.tabs[i].refedit_closed.clear(); @@ -267,7 +393,7 @@ impl OpenCADStudio { self.tabs[i] .scene .set_refedit_keep(Some(temp_handles.iter().copied().collect::>())); - self.tabs[i].scene.refedit_lock = true; + self.tabs[i].scene.refedit_lock = lock; self.tabs[i].scene.deselect_all(); self.tabs[i].dirty = true; // No active command — the definition's objects are edited freely @@ -375,6 +501,10 @@ impl OpenCADStudio { .iter() .filter_map(|h| self.tabs[i].scene.document.get_entity(*h).cloned()) .map(|mut entity| { + // Back to the reference's own dependent names. + rename_symbols(&mut entity, &|kind, name| { + session.names.original(kind, name).map(str::to_string) + }); apply_insert_inverse_transform(&mut entity, &session); let mut entity = normalize_entity_for_block(entity); entity.common_mut().handle = Handle::NULL; @@ -457,6 +587,7 @@ impl OpenCADStudio { } fn finish_refedit_close(&mut self, i: usize, session: RefEditSession) { + self.drop_refedit_names(i, &session.names); self.tabs[i].refedit_closed.push(session); self.tabs[i].scene.refedit_lock = false; self.tabs[i].scene.set_refedit_keep(None); @@ -586,8 +717,8 @@ impl OpenCADStudio { .map(|e| e.common().handle) .filter(|h| session.in_working_set(*h)) .collect(); + self.tabs[i].scene.refedit_lock = session.lock; self.tabs[i].refedit_session = Some(session); - self.tabs[i].scene.refedit_lock = true; self.tabs[i].scene.set_refedit_keep(Some(keep)); } } @@ -605,4 +736,193 @@ impl OpenCADStudio { _ => {} } } + + /// The working set's objects of an external reference under host names: + /// `XREF|NAME` becomes `$N$NAME` (the first free N) with records of its + /// own when `unique`, else `NAME`, sharing a host record of that name when + /// one exists. Missing records are copied from the reference's. + fn refedit_host_names( + &mut self, + i: usize, + mut copies: Vec, + prefix: &str, + unique: bool, + ) -> (Vec, RefEditNames) { + let document = &mut self.tabs[i].scene.document; + let deps = crate::app::ClipboardDeps::capture(document, &copies); + let strip = |name: &str| -> Option { + (name.len() > prefix.len() + && name.is_char_boundary(prefix.len()) + && name[..prefix.len()].eq_ignore_ascii_case(prefix)) + .then(|| name[prefix.len()..].to_string()) + }; + let exists = |document: &codec::CadDocument, kind: SymbolKind, name: &str| match kind { + SymbolKind::Layer => document.layers.contains(name), + SymbolKind::Linetype => document.line_types.contains(name), + SymbolKind::TextStyle => document.text_styles.contains(name), + SymbolKind::Block => document.block_records.get(name).is_some(), + }; + let wanted: Vec<(SymbolKind, String)> = deps + .layers + .iter() + .map(|r| (SymbolKind::Layer, r.name.clone())) + .chain(deps.linetypes.iter().map(|r| (SymbolKind::Linetype, r.name.clone()))) + .chain(deps.text_styles.iter().map(|r| (SymbolKind::TextStyle, r.name.clone()))) + .chain(deps.blocks.iter().map(|b| (SymbolKind::Block, b.name.clone()))) + .collect(); + let mut names = RefEditNames::default(); + for (kind, original) in wanted { + let Some(base) = strip(&original) else { + continue; + }; + let taken = |names: &RefEditNames, candidate: &str| { + exists(document, kind, candidate) + || names + .renamed + .iter() + .any(|(k, used, _)| *k == kind && used.eq_ignore_ascii_case(candidate)) + }; + // Layer 0 is the host's own in every drawing. + let used = if kind == SymbolKind::Layer && base == "0" { + base + } else if unique { + (0u32..) + .map(|n| format!("${n}${base}")) + .find(|candidate| !taken(&names, candidate)) + .expect("a free name") + } else { + base + }; + if !exists(document, kind, &used) + && !names + .created + .iter() + .any(|(k, n)| *k == kind && n.eq_ignore_ascii_case(&used)) + { + names.created.push((kind, used.clone())); + } + names.renamed.push((kind, used, original)); + } + let used_for = |kind: SymbolKind, name: &str| { + names + .renamed + .iter() + .find(|(k, _, original)| *k == kind && original.eq_ignore_ascii_case(name)) + .map(|(_, used, _)| used.clone()) + }; + let created = |kind: SymbolKind, name: &str| -> Option { + used_for(kind, name).filter(|used| { + names + .created + .iter() + .any(|(k, n)| *k == kind && n.eq_ignore_ascii_case(used)) + }) + }; + for record in &deps.layers { + if let Some(used) = created(SymbolKind::Layer, &record.name) { + let mut copy = record.clone(); + copy.name = used; + copy.flags.xref_dependent = false; + copy.xref_block_record_handle = Handle::NULL; + if let Some(linetype) = used_for(SymbolKind::Linetype, ©.line_type) { + copy.line_type = linetype; + } + copy.handle = document.allocate_handle(); + let _ = document.layers.add(copy); + } + } + for record in &deps.linetypes { + if let Some(used) = created(SymbolKind::Linetype, &record.name) { + let mut copy = record.clone(); + copy.name = used; + copy.xref_dependent = false; + copy.xref_block_record_handle = Handle::NULL; + copy.handle = document.allocate_handle(); + let _ = document.line_types.add(copy); + } + } + for record in &deps.text_styles { + if let Some(used) = created(SymbolKind::TextStyle, &record.name) { + let mut copy = record.clone(); + copy.name = used; + copy.xref_dependent = false; + copy.xref_block_record_handle = Handle::NULL; + copy.handle = document.allocate_handle(); + let _ = document.text_styles.add(copy); + } + } + let rename = |kind: SymbolKind, name: &str| used_for(kind, name); + let blocks: Vec = deps + .blocks + .iter() + .filter_map(|def| { + let used = created(SymbolKind::Block, &def.name)?; + let mut entities = def.entities.clone(); + for entity in &mut entities { + rename_symbols(entity, &rename); + } + Some(crate::app::BlockDef { + name: used, + base_point: def.base_point, + entities, + }) + }) + .collect(); + crate::app::command_driver::clipboard::merge_block_defs(document, &blocks); + for entity in &mut copies { + rename_symbols(entity, &rename); + } + (copies, names) + } + + /// Removes the host records made for a session that nothing uses any more. + fn drop_refedit_names(&mut self, i: usize, names: &RefEditNames) { + for (kind, name) in names.created.iter().rev() { + let scene = &mut self.tabs[i].scene; + let in_use = scene.document.entities().any(|entity| { + let common = entity.common(); + match kind { + SymbolKind::Layer => common.layer.eq_ignore_ascii_case(name), + SymbolKind::Linetype => common.linetype.eq_ignore_ascii_case(name), + SymbolKind::TextStyle => match entity { + EntityType::Text(t) => t.style.eq_ignore_ascii_case(name), + EntityType::MText(t) => t.style.eq_ignore_ascii_case(name), + EntityType::AttributeDefinition(a) => { + a.text_style.eq_ignore_ascii_case(name) + } + _ => false, + }, + SymbolKind::Block => matches!( + entity, + EntityType::Insert(insert) if insert.block_name.eq_ignore_ascii_case(name) + ), + } + }); + // A linetype made for the session may also be a kept layer's. + let in_use = in_use + || (*kind == SymbolKind::Linetype + && scene + .document + .layers + .iter() + .any(|layer| layer.line_type.eq_ignore_ascii_case(name))); + if in_use { + continue; + } + match kind { + SymbolKind::Layer => { + scene.document.layers.remove(name); + } + SymbolKind::Linetype => { + scene.document.line_types.remove(name); + } + SymbolKind::TextStyle => { + scene.document.text_styles.remove(name); + } + SymbolKind::Block => { + let _ = crate::scene::block_service::erase(scene, name); + } + } + } + } } diff --git a/src/app/control/actions.rs b/src/app/control/actions.rs index 6a4d48deb..893e35086 100644 --- a/src/app/control/actions.rs +++ b/src/app/control/actions.rs @@ -105,6 +105,7 @@ pub(super) const NAMES: &[&str] = &[ "pdf_dialog_ok", "view_background", "image_adjust", + "refedit_dialog", "pdf_layer_toggle", "pc_manager_toggle", "pc_manager", @@ -1885,6 +1886,36 @@ impl OpenCADStudio { }) } "ribbon_dropdown" => Message::ToggleRibbonDropdown(string(req, "value")?.into()), + // The Edit Reference dialog, one field per call: "level=" (0 = + // the picked reference), "nested=0|1" (pick the working set in the + // drawing), "attributes=0|1", "lock=0|1", "unique=0|1", "ok" or + // "cancel". + "refedit_dialog" => { + use crate::ui::window::refedit_dialog::RefEditDialogMsg as R; + if self.active_modal != Some(crate::app::ModalKind::RefEdit) { + return Err(failure("no_dialog", "The Reference Edit dialog is not open")); + } + let spec = string(req, "value")?; + let (key, value) = spec.split_once('=').unwrap_or((spec, "")); + let flag = || match value { + "1" => Ok(true), + "0" => Ok(false), + _ => Err(failure("invalid_value", spec)), + }; + if key == "cancel" { + Message::CloseModal + } else { + Message::RefEditDialog(match key { + "ok" => R::Ok, + "level" => R::Level(value.parse().map_err(|_| failure("invalid_value", spec))?), + "nested" => R::Nested(flag()?), + "attributes" => R::Attributes(flag()?), + "lock" => R::Lock(flag()?), + "unique" => R::Unique(flag()?), + _ => return Err(failure("invalid_value", "Unknown Reference Edit field")), + }) + } + } // The -VIEW Background dialog, one field per call: "type=gradient", // "color|top|middle|bottom=r,g,b", "three=1", "rotation=30", // "image=", "position=center|stretch|tile", "aspect=1", "offset=x,y" (pixels), "scale=x,y", diff --git a/src/app/mod.rs b/src/app/mod.rs index e7a768374..9ed184374 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -586,6 +586,8 @@ pub(super) struct OpenCADStudio { view_background: Option, /// IMAGEADJUST dialog. image_adjust: Option, + /// The Edit Reference dialog (REFEDIT). + refedit_dialog: Option, underlay_layers: Option, pdf_import_settings: Option, pdf_import_file: Option, @@ -794,6 +796,8 @@ pub(super) struct OpenCADStudio { block_freq: std::collections::HashMap, /// BLOCKMRULIST: how many recent blocks the palette keeps (0–100). pub(crate) block_mru_list: u8, + /// The Edit Reference dialog's remembered settings. + pub(crate) refedit_options: crate::app::settings::RefEditOptions, /// BLOCKREDEFINEMODE (0–2). pub(crate) block_redefine_mode: u8, /// BLOCKNAVIGATE: the Libraries tab's start folder ("." for none). @@ -2158,6 +2162,7 @@ pub enum ModalKind { PcSection, ViewBackground, ImageAdjust, + RefEdit, UnderlayLayers, PdfImportSettings, PdfImportFile, @@ -4356,6 +4361,8 @@ pub enum Message { PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg), AttdefDialog(crate::ui::window::attdef_dialog::AttdefDialogMsg), FieldDialog(crate::ui::window::field_dialog::FieldDialogMsg), + /// An edit in the Edit Reference dialog. + RefEditDialog(crate::ui::window::refedit_dialog::RefEditDialogMsg), /// A click or search in the Point Cloud Manager. PcManager(crate::ui::window::pc_manager::PcManagerMsg), Count(crate::ui::window::count_palette::CountMsg), @@ -4569,6 +4576,7 @@ impl OpenCADStudio { pc_section: None, view_background: None, image_adjust: None, + refedit_dialog: None, underlay_layers: None, pdf_import_settings: None, pdf_import_file: None, @@ -4668,6 +4676,7 @@ impl OpenCADStudio { block_mru: Vec::new(), block_freq: std::collections::HashMap::new(), block_mru_list: 50, + refedit_options: Default::default(), block_redefine_mode: 1, block_navigate: ".".to_string(), insname: String::new(), diff --git a/src/app/settings.rs b/src/app/settings.rs index 834c1874a..a63453fb1 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -652,6 +652,9 @@ pub struct UserSettings { /// BLOCKNAVIGATE (default "."). #[serde(default = "default_block_navigate")] pub block_navigate: String, + /// The Edit Reference dialog's settings. + #[serde(default)] + pub refedit: RefEditOptions, /// XFADECTL: how far objects outside the REFEDIT working set fade (0–90, default 70). #[serde(default = "default_xfadectl")] pub xfadectl: u8, @@ -687,6 +690,30 @@ pub struct PaletteBlockRef { pub drawing: bool, } +/// The Edit Reference dialog's settings, remembered between sessions: give +/// an external reference's clashing named objects unique names, check out +/// attribute definitions, lock what is outside the working set, pick the +/// working set's objects in the drawing. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(default)] +pub struct RefEditOptions { + pub unique_names: bool, + pub attributes: bool, + pub lock: bool, + pub nested: bool, +} + +impl Default for RefEditOptions { + fn default() -> Self { + Self { + unique_names: true, + attributes: false, + lock: true, + nested: false, + } + } +} + /// The Blocks palette's insertion options. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct BlockInsertOptions { @@ -938,6 +965,7 @@ impl Default for UserSettings { block_mru_list: 50, block_redefine_mode: 1, block_navigate: ".".to_string(), + refedit: RefEditOptions::default(), xfadectl: 70, count_color: 3, count_error_color: 1, diff --git a/src/app/update/file.rs b/src/app/update/file.rs index 4612a082c..c4cd4b69c 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -873,6 +873,7 @@ impl OpenCADStudio { block_mru_list: self.block_mru_list, block_redefine_mode: self.block_redefine_mode, block_navigate: self.block_navigate.clone(), + refedit: self.refedit_options.clone(), xfadectl: crate::scene::cache::block_cache::refedit_fade_ctl() as u8, count_color: self.count_palette.color, count_error_color: self.count_palette.error_color, @@ -1036,6 +1037,7 @@ impl OpenCADStudio { self.block_redefine_mode = s.block_redefine_mode.min(2); self.block_navigate = s.block_navigate.clone(); crate::scene::cache::block_cache::set_refedit_fade_ctl(i32::from(s.xfadectl.min(90))); + self.refedit_options = s.refedit.clone(); self.count_palette.color = s.count_color; self.count_palette.error_color = s.count_error_color; self.count_palette.service = s.count_service; diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index d82b32e60..41a2c3cbc 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -2281,6 +2281,7 @@ impl OpenCADStudio { Message::PdfDialog(message) => self.update_pdf_dialog(message), Message::AttdefDialog(message) => self.on_attdef_dialog(message), Message::FieldDialog(message) => self.on_field_dialog(message), + Message::RefEditDialog(message) => self.update_refedit_dialog(message), Message::PcManager(message) => self.update_pc_manager(message), Message::Count(message) => self.on_count(message), Message::SheetSet(message) => self.on_sheet_set(message), @@ -9503,6 +9504,9 @@ impl OpenCADStudio { Task::none() }; } + if self.active_modal == Some(super::ModalKind::RefEdit) { + self.refedit_dialog = None; + } // Cancelling the section settings drops the waiting extraction. if self.active_modal == Some(super::ModalKind::PcSection) { crate::modules::insert::pc_extract::drop_job(); diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index 96beed812..b4a8d1501 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -41,6 +41,7 @@ impl OpenCADStudio { self.view_background.as_ref().map_or("", |s| s.view.as_str()) ), Some(K::ImageAdjust) => crate::tr!("image-adjust", "title"), + Some(K::RefEdit) => crate::tr!("ref-edit", "dialog-title"), Some(K::UnderlayLayers) => crate::t!("Underlay Layers").into_owned(), Some(K::PdfImportSettings) => crate::t!("PDF Import Settings").into_owned(), Some(K::PdfImportFile) => crate::t!("Import PDF").into_owned(), @@ -542,6 +543,12 @@ impl OpenCADStudio { crate::ui::window::pdf_dialogs::image_adjust(state, flow) }) } + super::super::ModalKind::RefEdit => { + let state = self.refedit_dialog.as_ref()?; + sized_flow(ex, 560, 470, |flow| { + crate::ui::window::refedit_dialog::view(state, flow) + }) + } super::super::ModalKind::UnderlayLayers => { let state = self.underlay_layers.as_ref()?; sized_flow(ex, 460, 520, |flow| { diff --git a/src/mcp.rs b/src/mcp.rs index ddb2fe933..e44f86b75 100644 --- a/src/mcp.rs +++ b/src/mcp.rs @@ -32,7 +32,7 @@ const MODERN_PROTOCOL_VERSION: &str = "2026-07-28"; /// /// `block_delete` gained its optional `force` parameter. #[cfg(test)] -const TOOL_SCHEMA_DIGEST: &str = "dde915fd17f3cc4667e1eee8d796030156e7ff9423b0fa9b48a648a1481df723"; +const TOOL_SCHEMA_DIGEST: &str = "c5f9ad00e04c0acc809c7de0c75e789fd9bf3ad6db68bdde36bd771a859fef79"; const MAX_REQUEST: usize = 1_048_576; const MAX_RESPONSE: u64 = 16 * 1024 * 1024; const CACHE_TTL_MS: u64 = 3_600_000; diff --git a/src/modules/draw/modify/refedit.rs b/src/modules/draw/modify/refedit.rs index 87396775f..b1d25e969 100644 --- a/src/modules/draw/modify/refedit.rs +++ b/src/modules/draw/modify/refedit.rs @@ -94,6 +94,79 @@ pub struct RefEditSession { pub handle_watermark: u64, /// The referenced file when the definition is an xref. pub xref_path: Option, + /// Objects outside the working set can't be selected while it is open. + pub lock: bool, + /// The external reference's named objects the working set uses under + /// host names, and the host records made for the session. + pub names: RefEditNames, +} + +/// A kind of named object the working set's objects refer to. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SymbolKind { + Layer, + Linetype, + TextStyle, + Block, +} + +/// How an external reference's dependent names (`XREF|NAME`) are used in +/// the host while its objects are checked out: as `$0$NAME` records of +/// their own (unique names), or under the plain name, sharing a host record +/// of that name when there is one. +#[derive(Debug, Clone, Default)] +pub struct RefEditNames { + /// (kind, name used in the host, the reference's dependent name). + pub renamed: Vec<(SymbolKind, String, String)>, + /// Records added to the host for the session, removed when it closes. + pub created: Vec<(SymbolKind, String)>, +} + +impl RefEditNames { + /// The reference's own name for a host name used in the session. + pub fn original(&self, kind: SymbolKind, name: &str) -> Option<&str> { + self.renamed + .iter() + .find(|(k, used, _)| *k == kind && used.eq_ignore_ascii_case(name)) + .map(|(_, _, original)| original.as_str()) + } +} + +/// Rewrites every named-object reference of an entity through `map` +/// (layer, linetype, text style, block). +pub fn rename_symbols(entity: &mut EntityType, map: &dyn Fn(SymbolKind, &str) -> Option) { + let common = entity.common_mut(); + if let Some(name) = map(SymbolKind::Layer, &common.layer) { + common.layer = name; + common.layer_handle = None; + } + if let Some(name) = map(SymbolKind::Linetype, &common.linetype) { + common.linetype = name; + common.linetype_handle = None; + } + match entity { + EntityType::Insert(insert) => { + if let Some(name) = map(SymbolKind::Block, &insert.block_name) { + insert.block_name = name; + } + } + EntityType::Text(text) => { + if let Some(name) = map(SymbolKind::TextStyle, &text.style) { + text.style = name; + } + } + EntityType::MText(text) => { + if let Some(name) = map(SymbolKind::TextStyle, &text.style) { + text.style = name; + } + } + EntityType::AttributeDefinition(attdef) => { + if let Some(name) = map(SymbolKind::TextStyle, &attdef.text_style) { + attdef.text_style = name; + } + } + _ => {} + } } impl RefEditSession { @@ -334,10 +407,23 @@ pub struct RefEditOptionsCommand { level: usize, step: OptionStep, nested: Vec, + /// The session settings (lock objects outside the working set, unique + /// names for an external reference's named objects). + lock: bool, + unique: bool, + /// Set when the Edit Reference dialog already chose the attribute + /// option: the nested pick then opens the session directly. + attributes: Option, } impl RefEditOptionsCommand { - pub fn new(top: Handle, chain: Vec, hits: Vec) -> Self { + pub fn new( + top: Handle, + chain: Vec, + hits: Vec, + lock: bool, + unique: bool, + ) -> Self { let level = chain.len().saturating_sub(1); Self { top, @@ -346,9 +432,30 @@ impl RefEditOptionsCommand { level, step: OptionStep::Nesting, nested: Vec::new(), + lock, + unique, + attributes: None, } } + /// `Select nested objects:` for the dialog's "pick in the drawing" + /// working set at `level`. + pub fn nested_pick( + top: Handle, + chain: Vec, + hits: Vec, + level: usize, + attributes: bool, + lock: bool, + unique: bool, + ) -> Self { + let mut command = Self::new(top, chain, hits, lock, unique); + command.level = level.min(command.chain.len().saturating_sub(1)); + command.step = OptionStep::Nested; + command.attributes = Some(attributes); + command + } + fn begin(&self, attributes: bool) -> CmdResult { let path: Vec = self.chain[1..=self.level] .iter() @@ -357,11 +464,13 @@ impl RefEditOptionsCommand { let nested: Vec = self.nested.iter().map(|h| h.value().to_string()).collect(); CmdResult::Relaunch( format!( - "REFEDIT_BEGIN:{}:{}:{}:{}", + "REFEDIT_BEGIN:{}:{}:{}:{}:{}:{}", self.top.value(), path.join(","), attributes as u8, - nested.join(",") + nested.join(","), + self.lock as u8, + self.unique as u8 ), vec![], ) @@ -469,6 +578,9 @@ impl CadCommand for RefEditOptionsCommand { if n == 0 { return CmdResult::CancelWithMessage(t!("0 items selected").into_owned()); } + if let Some(attributes) = self.attributes { + return self.begin(attributes); + } self.step = OptionStep::Attributes; CmdResult::ReportMeasurement(if n == 1 { t!("1 item selected").into_owned() diff --git a/src/ui/window/mod.rs b/src/ui/window/mod.rs index a2a55c9f9..22943e433 100644 --- a/src/ui/window/mod.rs +++ b/src/ui/window/mod.rs @@ -30,6 +30,7 @@ pub mod plot; pub mod plugin_manager; pub mod print_all; pub mod recovery; +pub mod refedit_dialog; pub mod sheet_set; pub mod shortcuts; pub mod update_notice; diff --git a/src/ui/window/pdf_dialogs.rs b/src/ui/window/pdf_dialogs.rs index 5acaaf136..8c4d8932e 100644 --- a/src/ui/window/pdf_dialogs.rs +++ b/src/ui/window/pdf_dialogs.rs @@ -312,7 +312,7 @@ pub(crate) fn well_style(theme: &Theme) -> container::Style { } /// A titled card. -fn card<'a>(title: String, content: impl Into>) -> Element<'a, Message> { +pub(crate) fn card<'a>(title: String, content: impl Into>) -> Element<'a, Message> { container( column![ text(title.to_uppercase()).size(10).style(accent_text), diff --git a/src/ui/window/refedit_dialog.rs b/src/ui/window/refedit_dialog.rs new file mode 100644 index 000000000..e4fdb2fd3 --- /dev/null +++ b/src/ui/window/refedit_dialog.rs @@ -0,0 +1,247 @@ +//! Edit Reference dialog (REFEDIT): the nesting level to check out, shown as +//! a chain of chips beside a preview of the chosen definition, how the +//! working set is filled, whether attribute definitions come along, and the +//! session settings (lock what is outside the working set, give clashing +//! named objects of an external reference unique names). + +use iced::widget::{button, column, container, row, text, toggler, Space}; +use iced::{Border, Element, Fill, Length, Theme}; + +use crate::app::Message; +use crate::scene::model::wire_model::WireModel; +use crate::ui::style::common::muted_style; +use crate::ui::style::form::{button_style, dialog_button}; +use crate::ui::window::pdf_dialogs::{card, well_style}; + +/// One edit in the Edit Reference dialog. +#[derive(Debug, Clone)] +pub enum RefEditDialogMsg { + /// The nesting level picked in the chain (0 = the selected reference). + Level(usize), + /// Pick the working set's objects in the drawing (else all of them). + Nested(bool), + Attributes(bool), + Lock(bool), + Unique(bool), + Help, + Ok, +} + +fn msg(m: RefEditDialogMsg) -> Message { + Message::RefEditDialog(m) +} + +/// One level of the nesting chain as the dialog shows it. +pub struct RefEditLevel { + /// The reference's handle: the picked object for level 0, the + /// definition-owned reference below it. + pub insert: codec::Handle, + pub name: String, + /// The definition's preview. + pub wires: Vec, + /// The definition holds attribute definitions (the attribute switch is + /// only offered then). + pub has_attdefs: bool, + /// The referenced file of an external reference. + pub xref_path: Option, +} + +pub struct RefEditDialogState { + pub top: codec::Handle, + /// The picked chain, outermost first. + pub chain: Vec, + pub levels: Vec, + pub level: usize, + pub nested: bool, + pub attributes: bool, + pub lock: bool, + pub unique: bool, +} + +impl RefEditDialogState { + /// The attribute switch counts only where the chosen definition has + /// attribute definitions. + pub fn attributes_enabled(&self) -> bool { + self.levels.get(self.level).is_some_and(|l| l.has_attdefs) + } +} + +fn label<'a>(value: String, enabled: bool) -> Element<'a, Message> { + text(value) + .size(12) + .style(move |theme: &Theme| { + if enabled { + text::Style::default() + } else { + text::Style { + color: Some(theme.palette().background.strong.color), + } + } + }) + .width(Fill) + .into() +} + +/// A labelled switch on one row; inactive while `on_toggle` is None. +fn switch_row<'a>( + name: String, + value: bool, + on_toggle: Option RefEditDialogMsg>, +) -> Element<'a, Message> { + let enabled = on_toggle.is_some(); + let mut switch = toggler(value).size(18.0); + if let Some(on) = on_toggle { + switch = switch.on_toggle(move |v| msg(on(v))); + } + row![label(name, enabled), switch] + .spacing(8) + .align_y(iced::Center) + .into() +} + +/// A level of the chain as a pill; the chosen one framed in the accent colour. +fn level_chip<'a>(name: &str, index: usize, chosen: bool) -> Element<'a, Message> { + button(text(name.to_string()).size(12)) + .on_press(msg(RefEditDialogMsg::Level(index))) + .padding([5, 12]) + .style(move |theme: &Theme, status| { + let mut style = button_style(false)(theme, status); + style.border = Border { + width: if chosen { 1.5 } else { 1.0 }, + radius: 14.0.into(), + color: if chosen { + theme.palette().primary.base.color + } else { + theme.palette().background.strong.color + }, + }; + if chosen { + style.text_color = theme.palette().primary.base.color; + } + style + }) + .into() +} + +pub fn view<'a>( + state: &'a RefEditDialogState, + sizing: crate::ui::modal::ModalSizing, +) -> Element<'a, Message> { + let current = state.levels.get(state.level); + let preview: Element<'a, Message> = container(match current { + Some(level) => crate::ui::window::block_palette::preview_canvas(&level.wires, 104.0), + None => Space::new().into(), + }) + .width(Length::Fixed(150.0)) + .height(Length::Fixed(104.0)) + .padding(4) + .style(well_style) + .into(); + let mut chips = row![].spacing(6).align_y(iced::Center); + for (index, level) in state.levels.iter().enumerate() { + if index > 0 { + chips = chips.push(text("›").size(13).style(muted_style)); + } + chips = chips.push(level_chip(&level.name, index, index == state.level)); + } + let mut about = column![text(crate::tr!("ref-edit", "dialog-chain-hint")) + .size(11) + .style(muted_style)] + .spacing(4); + if let Some(path) = current.and_then(|l| l.xref_path.as_deref()) { + // A long path keeps its end, where the file name is. + const MAX: usize = 64; + let count = path.chars().count(); + let shown = if count > MAX { + format!("…{}", path.chars().skip(count - (MAX - 1)).collect::()) + } else { + path.to_string() + }; + about = about.push( + text(format!("{} {}", crate::tr!("ref-edit", "dialog-path"), shown)) + .size(11) + .style(muted_style), + ); + } + let reference = card( + crate::tr!("ref-edit", "dialog-reference"), + row![ + preview, + column![iced::widget::scrollable(chips).direction( + iced::widget::scrollable::Direction::Horizontal( + iced::widget::scrollable::Scrollbar::new().width(4).scroller_width(4) + ) + ), about] + .spacing(8) + .width(Fill) + ] + .spacing(12), + ); + + let segment = |nested: bool, name: String| { + button(text(name).size(12).width(Fill).align_x(iced::Center)) + .on_press(msg(RefEditDialogMsg::Nested(nested))) + .style(button_style(state.nested == nested)) + .padding([6, 8]) + .width(Fill) + }; + let method = container( + row![ + segment(false, crate::tr!("ref-edit", "dialog-all-objects")), + segment(true, crate::tr!("ref-edit", "dialog-pick-objects")), + ] + .spacing(2), + ) + .padding(2) + .width(Fill) + .style(well_style); + let attributes_enabled = state.attributes_enabled(); + let working_set = card( + crate::tr!("ref-edit", "dialog-working-set"), + column![ + method, + switch_row( + crate::tr!("ref-edit", "dialog-attributes"), + state.attributes && attributes_enabled, + attributes_enabled.then_some(RefEditDialogMsg::Attributes as fn(bool) -> _), + ), + ] + .spacing(10), + ); + let session = card( + crate::tr!("ref-edit", "dialog-session"), + column![ + switch_row( + crate::tr!("ref-edit", "dialog-lock"), + state.lock, + Some(RefEditDialogMsg::Lock), + ), + switch_row( + crate::tr!("ref-edit", "dialog-unique"), + state.unique, + Some(RefEditDialogMsg::Unique), + ), + ] + .spacing(10), + ); + let footer = row![ + button(text("?").size(12)) + .on_press(msg(RefEditDialogMsg::Help)) + .style(button_style(false)) + .padding([5, 11]), + Space::new().width(Fill), + dialog_button(crate::t!("Cancel"), Message::CloseModal, false), + dialog_button( + std::borrow::Cow::Owned(crate::tr!("ref-edit", "dialog-start")), + msg(RefEditDialogMsg::Ok), + true + ), + ] + .spacing(6) + .align_y(iced::Center); + column![reference, working_set, session, footer] + .spacing(10) + .padding([10, 12]) + .width(sizing.width) + .into() +}