---
# System prepended metadata

title: Metting

---

Metting
===
<i class="fa fa-calendar fa-lg"></i> 2023-06-08
<br/>
![](https://imgur.com/77oLka4.png)
<br/>
<i class="fa fa-calendar fa-lg"></i> 2023-05-18
:::info
### [BubbleInfo]
```cpp=
let info = BubbleInfo(targetView: view,
                      anchorPosition: .right,
                      bubbleController: TutorialBodyEffectController())
bodyEffectBubble = TutorialBubble(info: info)
```
### [TutorialBubbleController]
```cpp=
class TutorialBubbleController: NSObject, TutorialBubbleControllerProtocol {
    var isHiddenPreview:Bool { return false }
    var isHiddenTitle:Bool { return false }
    var isHiddenDescription:Bool { return false }
    var isHiddenHyperlink:Bool { return false }
    var isHiddenTryItButton:Bool { return false }
    
    var titleText:String { return "" }
    var descriptionText:String { return "" }
    var canShowBubble:Bool { return false }
    
    var demoVideoFileName:String? { return nil }
    
    func bubbleWillShow() {}
    func bubbleWillClose() {}
}

final class TutorialBodyEffectController: TutorialBubbleController {
    override var isHiddenTryItButton:Bool { return true }
    
    override var titleText: String {
        return NSLocalizedString("Body Effect", comment: "")
    }
    
    override var descriptionText:String {
        return NSLocalizedString("Apply background and wraparound video effects around moving objects.", comment: "")
    }
    
    override var canShowBubble:Bool {
        return !StatusCenter.shared.isBubbleShowing()
    }
    
    override var demoVideoFileName:String? {
        return "Tutorial_BodyEffect"
    }
    
    override func bubbleWillClose() {
        sendCEIPBubbleTried()
    }
    
    private func sendCEIPBubbleTried() {
        let ceip = CEIPBubbleTried(.bodyEffect, .close)
        ceip.send()
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-05-04
:::info
### [ObjectMaskDetector]
```cpp=
class ObjectMaskDetector {
    init(sourceImage:NSBitmapImageRep?) {/**/}
    init(sourcePathURL:URL?) {/**/}
    
    func detect()->Bool {/**/}
    
    func getMask()->MTLTexture? {/**/}
    func getMask()->NSImage? {/**/}
    func getMask()->CGImage? {/**/}
}
```
### [how to use]
#### [BrushMaskDesignerController]
```cpp=
func applyAutoObjectSelectionMask() -> Bool {
    //ignore...
    guard let pictureBIR = getPictureBitmapImageRep() else { return false }
    
    let detector = ObjectMaskDetector(sourceImage: pictureBIR)
    guard detector.detect() else { return false }
        
    guard let newTex:MTLTexture = detector.getMask(),
              var maskAttr = picture.getValue(forKey: PictureParaKey.kMaskAttr) as? PictureDefinition.MaskAttr else { return false }
    
    //ignore...
    
    maskAttr.buffer = newTex
        _ = picture.setValue(maskAttr, forKey: PictureParaKey.kMaskAttr)
    //ignore...
    return true
}
```
#### [TextToStickerViewController]
```cpp=
private func executeObjectDetection() {
    let detector = ObjectMaskDetector(sourcePathURL: sourceImagePathURL)
    guard detector.detect() else { return }
        
    if let texture:MTLTexture = detector.getMask() {
        maskImagePathURL = MaskUtility.saveMaskToFile(texture: texture)
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-04-06
:::info
### [TutorialTargetView]
![](https://i.imgur.com/AgW1HzR.png)
#### [how to use]
```cpp=
tutorialTargetView.initView(superView: titleRoomButton.superview,
                            regionViews: [titleRoomButton,
                                          transitionRoomButton,
                                          effectRoomButton,
                                          pipRoomButton,
                                          particleRoomButton])
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-03-30
:::info
### [TutorialBubble]
#### [class]
```cpp=
class TutorialBubble: NSObject {
    init(info:BubbleInfo) {/*ignore*/}
    func show() {/*ignore*/}
    func close() {/*ignore*/}
}
```
#### [bubbleInfo]
```cpp=
struct BubbleInfo {
    var targetView:NSView
    var anchorPosition:AnchorPosition
    var bubbleStoryboardName:String
    var bubbleStoryboardID:String? = nil
    var targetViewHighlightColor:CGColor? = nil
    var bubbleBackgroundColor:CGColor? = nil
}
```
#### [protocol]
```cpp=
@objc protocol TutorialBubbleDelegate: AnyObject {
    @objc optional func bubbleWillShow()
    @objc optional func bubbleWillClose()
}
```
#### [how to use]
```cpp=
let info = BubbleInfo(targetView: view,
                      anchorPosition: .right,
                      bubbleStoryboardName: "BodyEffectBlueBubble")
bodyEffectBubble = TutorialBubble(info: info)

if let bodyEffectBubble = bodyEffectBubble {
    bodyEffectBubble.show()
}
```
:::
:::info
### [TutorialBubbleViewController]
#### [class]
```cpp=
class TutorialBubbleViewController: NSViewController, TutorialBubbleDelegate {
    func getTutorialBubble()->TutorialBubble? {
        return tutorialBubble
    }
    
    func canShowBubble()->Bool { assert(false); return true }/*need to override by derived class*/
}
```

#### [how to use]
```cpp=
class BodyEffectBlueBubbleViewController: TutorialBubbleViewController {
    override func canShowBubble()->Bool {
        return isWebViewLoadFinish && !isBlueBubbleShown
    }
}

extension BodyEffectBlueBubbleViewController {
    func bubbleWillShow() {
        isBlueBubbleShown = true
        NotificationCenter.default.post(name: .bubbleWillShow, object: self)
    }
    
    func bubbleWillClose() {
        sendCEIPBubbleTried()
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-03-23
:::info
### [CEIPTimelineEnumerator]
#### [protocol]
```cpp=
protocol CEIPEnumerateTimelineProtocol {
    func enumerating(tlObject:TLObject?)
    func execute()
}

final class CEIPTimelineEnumerator {
    //
}
```
#### [need to confirm protocol]
```cpp=
extension CEIPProduceUseContent : CEIPEnumerateTimelineProtocol {
    func enumerating(tlObject:TLObject?) {
        guard let tlObject = tlObject else { return }
        
        let newInfos = createContentInfo(tlObject: tlObject)
        if !newInfos.isEmpty {
            newInfos.forEach { newInfo in
                if let info = infos.first(where: {$0 == newInfo}) {
                    info.count += 1
                } else {
                    infos.append(newInfo)
                }
            }
        }
    }
    
    func execute() {
        send()
    }
}
```
#### [how to use]
```cpp=
let enumerator = CEIPTimelineEnumerator(senders: [CEIPProduceTimelineEdit(), CEIPProduceUseContent()])
enumerator.execute()
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-03-09
:::info
### [current]
```cpp=
class Entity: EditBaseObject {
    var title: String?
}

class IADEntity: Entity {
    var uniqueGUID: String? // Different with each aspect ratio
    var sharedGUID: String? // Same with each aspect ratio
    var categories: [CMSTemplateListResponseSubField_Categories] = []
}

class BaseEffectEntity: IADEntity {
    var effectId = ""
    var categoryID = ""
    var category = ""
}

class TemplateEntity: IADEntity {
    var categoryID: String?
    var category: String?
    var directorZoneTemplateID: String = ""
    var DZParentTemplateID: String = ""
}
```
### [CEIPContentData]
```cpp=
struct CEIPContentData : Codable {
    var contentID:String? = nil
    var contentName:String? = nil
    var categoryID:String? = nil
    var categoryName:String? = nil
}
```
<i class="fa fa-calendar fa-lg"></i> 2023-03-02
:::info
### [CEIPMgr]
```cpp=
struct ShutterStockIDList {
    var VideoIDList: [String]
    var PhotoIDList: [String]
    var AudioIDList: [String]
}

@objc class CEIPMgr: NSObject {
    @objc static let shared = CEIPMgr()
    private let CL_VENDOR_ID = "D8D760AC-ACA2-493e-9623-61E9D47DE89C"
    private let exec: String = "PowerDirector Mac \(MiscUtility.getVersionString(isMajorOnly: false))"
    private let crashEventName = "BOOMERANG_SESSION_SUCCESS"
    private var strLaunchMode = ""
    
    override init() {
        /*do something*/
    }
    
    func updateMemberIdAndRatePlan() -> Int {
        /*do something*/
    }

    func deinitCEIPMgr() {
        /*do something*/
    }
    
    func sendCEIPUOpen() {
        /*do something*/
    }
    
    func getUUID() -> String {
        /*do something*/
    }
    
    // Only used by app store build currently
    @objc func sendCrashMsg() {
        /*do something*/
    }
    
    func sendCEIPFirstSignInResult(_ isSuccess: Bool) {
        /*do something*/
    }
    
    func sendCEIPSession() {
        /*do something*/
    }
    
    func sendCEIPTimelineInfo() {
        /*do something*/
    }
    
    func sendCEIPStockUsage(_ source: StockUsage.Source, _ stockId: String, _ stockType: StockUsage.StockType) {
        /*do something*/
    }
    
    func sendCEIPProduceUseIntroTemplate(_ list: [Int64]) {
        /*do something*/
    }
    
    func sendIntroCEIP(info: IntroCEIP.BaseInfo) {
        /*do something*/
    }
    
    func sendSTTCEIP(info: STTCEIP.BaseInfo) {
        /*do something*/
    }
    
    func sendClickShoppingCart() {
        /*do something*/
    }
    
    func sendRatingDialogInfo(eventName: String, content: [String: Any] = [:]) {
        /*do something*/
    }
    
    func setLaunchMode(_ mode: LaunchMode) {
        /*do something*/
    }
    
    private func licenseForShutterStockContent() {
        /*do something*/
    }
    
    private func getShutterStockContent(TLObjectList: [TLObject]) -> ShutterStockIDList {
        /*do something*/
    }

    func sendTitleDesignerUsage(hasMultipleBorder: Bool, hasColorGradient: Bool) {
        /*do something*/
    }

    func sendPipDesignerUsage(_ content: [String: Any]) {
        /*do something*/
    }

    func sendCEIPInfo(eventName: String,
                      version: String = "1",
                      content: [String: Any] = [:]) {

        /*do something*/
    }
    
    private func sendCEIPInfo(_ eventName: String, _ version: String, _ content: [String: Any], _ forceSend: Bool = false) {
        /*do something*/
    }
    
    private func sendCEIPInfo(_ content: String, _ eventName: String = "") {
        /*do something*/
    }
    
    private func sendCEIPUClose() {
        /*do something*/
    }
    
    private func sendOpenMsg() {
        /*do something*/
    }
    
    private func sendCEIPOnOff() {
        /*do something*/
    }
}
```
### [Client side]
#### [LibraryViewController]
```cpp=
@IBAction func switchRoom(_ sender: StatusButton) {

        let controller = currentRoomController
        let ip = lastSelectedIpPage

        onSwitchRoom(sender)

        if controller != currentRoomController {
            switch currentRoomController {
            case is MediaRoomController:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_MEDIA_ROOM)
            case is TitleRoomController:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_TITLE_ROOM)
            case is TransitionRoomController:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_TRANSITION_ROOM)
            case is EffectRoomController:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_EFFECT_ROOM)
            case is PipRoomController:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_PIP_ROOM)
            case is ParticleRoomController:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_PARTICLE_ROOM)
            default:
                break
            }
        }
        if currentRoomController == nil,
            ip != lastSelectedIpPage {
            switch lastSelectedIpPage {
            case .IP_MIXER:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_AUDIO_MIX_ROOM)
            case .IP_VOICEOVER:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_VOICE_RECORD_ROOM)
            case .IP_SUBTITLE:
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_SUBTITLE_ROOM)
                CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_SUBTITLE)
            default:
                break
            }
        }
    }
```
#### [StockMediaController]
```cpp=
private func sendCEIPStockUsage(_ entity: ShutterstockEntity) {
        guard let stockId = entity.id else { return }
        guard let mediaType = entity.media_type else { return }
        guard [.Shutterstock, .GettyImages, .GettyImagesPay].contains(entity.poolType) else { return }
        var source = StockUsage.Source.ShutterStock
        if entity.poolType == .Shutterstock {
            source = .ShutterStock
        } else if entity.poolType == .GettyImages {
            source = .GettyImagesFree
        } else if entity.poolType == .GettyImagesPay {
            source = .GettyImagesPremium
        }
        var stockType = StockUsage.StockType.ImageStock
        if mediaType == .Photo {
            stockType = StockUsage.StockType.ImageStock
        } else if mediaType == .Video {
            stockType = StockUsage.StockType.VideoStock
        } else if mediaType == .Music {
            stockType = StockUsage.StockType.MusicStock
        }
        CEIPMgr.shared.sendCEIPStockUsage(source, stockId, stockType)
}

//CEIPMgr
func sendCEIPStockUsage(_ source: StockUsage.Source, _ stockId: String, _ stockType: StockUsage.StockType) {
        let event = "Stock_Usage"
        let version = "1"
        let dateformat = DateFormatter()
        dateformat.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
        let usage_date = dateformat.string(from: Date())
        let content: [String: Any] = [
            "source": source.rawValue,
            "stock_id": stockId,
            "stock_type": stockType.rawValue,
            "usage_date": usage_date
        ]
        sendCEIPInfo(event, version, content, true)
}
```
:::
:::info
### [Design]
#### [CEIPAnalyzer][Interface]
```cpp=
protocol CEIPAnalyzer {
    func send()
}
```
#### [CEIPStockUsage][derived class]
```cpp=
class CEIPStockUsage : CEIPAnalyzer {
    private var entity:ShutterstockEntity?
    
    init(entity: ShutterstockEntity) {
        self.entity = entity
        
        /*do something*/
    }
    
    func send() {
        /*send info via CEIPMgr*/
        CEIPMgr.shared.sendCEIPInfo(event, version, content, true)
    }
}
```
#### [Client side][StockMediaController]
```cpp=
private func sendCEIPStockUsage(_ entity: ShutterstockEntity) {
    let ceip = CEIPStockUsage(entity: entity)
    ceip.send()
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-01-05
:::info
### [layout]
![](https://i.imgur.com/Fn5z6zx.png)
### [AlertIconView]
```cpp=
/* APPKit.NSAlert
extension NSAlert {
    public enum Style : UInt, @unchecked Sendable {
        case warning = 0
        case informational = 1
        case critical = 2
    }
}
*/
class AccountDeletionViewController: CloudViewController {
    @IBOutlet weak var iconView: AlertIconView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //
        iconView.initIcon(alertStyle: .critical)
        //
    }
}
```
:::

<i class="fa fa-calendar fa-lg"></i> 2022-02-17
### [custom folder path]
/Users/{user name}/Library/Group Containers/{id}.com.cyberlink.powerdirector/Library/Application Support/MyIntros/**Custom**/

### [download folder path]
/Users/{user name}/Library/Group Containers/{id}.com.cyberlink.powerdirector/Library/Application Support/MyIntros/**Preset**/

:::info
### [IntroUploadHandler]
```cpp=
protocol IntroUploadHandlerProtocol : class {
    func exportProgress(progress:Double)
    func exportComplete(isSucceed:Bool, isUserAbort:Bool)
}

class IntroUploadHandler {
    weak var delegate:IntroUploadHandlerProtocol? = nil
    
    func exportUniversalTemplate(packedFolderPathURL:URL) {/**/}
    func rollbackToOldIntroMovie() {/**/}
    func cancelExportUniversalTemplate() {/**/}
    func removeUploadedFiles() {/**/}
    func getUniversalTemplateUUID()->String? {/**/}
}
```
### [sample code]
```cpp=
class XXXClass {
    private var uploader:IntroUploadHandler? = nil
    
    func initDialog(controller:IntroController) {
        uploader = IntroUploadHandler(controller: controller)
        uploader?.delegate = self
    }
    
    func onUploadClicked(_ sender: Any) {
        //...
        uploader?.exportUniversalTemplate(packedFolderPathURL: packedFolderPathURL)
        //...
    }
    
    func onCancelUploadClicked(_ sender: Any) {
        //...
        uploader?.cancelExportUniversalTemplate()
        //...
    }
}

extension XXXClass : IntroUploadHandlerProtocol {
    //run on work thread
    func exportProgress(progress:Double) {//0.0-1.0
        DispatchQueue.main.async {
            print("[upload]progress=\(progress)")
        } 
    }
    
    //run on main thread
    func exportComplete(isSucceed:Bool, isUserAbort:Bool) {
        print("[upload]exportComplete")
        if isUserAbort {
            uploader?.removeUploadedFiles()
        }
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2021-10-05
:::info
### [AVIProfileTable]
```cpp=
{AVI_BEGIN_INDEX+5,_T("AUDIO"), _T(""),_T(""),_T(""),IDS_AVI_HIGH_QUALITY,IDS_PRODUCE_WAV_1024K,IDS_MPEG1_K_LBR_FAST_RECOM,
IDS_MPEG1_K_LBR_FAST_RECOM_DV , BASIC_GROUP,FVFC_WAV_AUDIO, SF_AUDIO, FIELD_FRAME,
0X00000082,
_T("\0"),   // Compressor Name
_T("PCM"),  // AudioCompressor Name
0,     // KeyFrameRate
30.0,   // FramePerSecond
0,    // Quality
0, // Data rate
640,    // Width
480,    // Height
24,     // Color bits
&(AVIProfileExt[4]),
NULL,
0
}
```
### [AVIProfileExt]
```cpp=
{//AVIProfileExt[4]
    WAVE_FORMAT_PCM, // Wave format
    2,      // channel
    32000,  // HZ
    128000, // Average bytes per second
    4,        // nBlockAlign
    16,        // bits per sample
    0,
}
```
:::

<i class="fa fa-calendar fa-lg"></i> 2021-02-18
:::info
### [SpinTimeEdit.swift]
```cpp=
@objc protocol SpinTimeEditTextFieldDelegate : class {
    @objc optional func pressOK()
    @objc optional func pressCancel()
}
```
### [CloseWindowHandlerViewController.swift]
```cpp=
class CloseWindowHandlerViewController: NSViewController {
    
    private var response:NSApplication.ModalResponse = .cancel
    
    func getResponse()->NSApplication.ModalResponse {
        return response
    }
    
    func setResponse(response:NSApplication.ModalResponse) {
        self.response = response
    }
}
```
### [DurationSettingDlg.swift]
```cpp=
class DurationSettingDlg: CloseWindowHandlerViewController {
    @IBOutlet weak var spinTimeEdit: SpinTimeEdit!
    
    private func initSpinTimeEdit() {
        //...
        spinTimeEdit.delegate = self
    }
    
    @IBAction func onOK(_ sender: Any) {
        setResponse(response: .OK)
        self.view.window?.performClose(self)
    }
    
    private func onCancel() {
        setResponse(response: .cancel)
        self.view.window?.performClose(self)
    }
}

extension DurationSettingDlg: SpinTimeEditTextFieldDelegate {
    func pressOK() {
        onOK(self)
    }
    
    func pressCancel() {
        onCancel()
    }
}

extension DurationSettingDlg: NSWindowDelegate {
    func windowWillClose(_ notification: Notification) {
        NSApplication.shared.stopModal(withCode: getResponse())
    }
}
```

<i class="fa fa-calendar fa-lg"></i> 2020-11-19
:::info
### [DeviceChecker.swift]
```cpp=
class DeviceChecker {
    enum VendorID {
        case intel
        case amd
        case nvKepler
    }
    
    static let shared = DeviceChecker()
    
    private init() {}
    private let cpuChecker = CPUChecker()
    private let gpuChecker = GPUChecker()
    
    //MARK:public
    func isIntelCPU()->Bool {
        return cpuChecker.isIntelCPU()
    }
    
    func isARMCPU()->Bool {
        return cpuChecker.isARMCPU()
    }
    
    func getGPUVendorIDs()->[VendorID] {
        return gpuChecker.getVendorIDs()
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2020-10-29
:::info
### [PerformanceTuning.swift]
```cpp=
typealias PT = PerformanceTuning
typealias AutoPT = AutoPerformanceTuning

class PerformanceTuning {
//...
}

class AutoPerformanceTuning : PerformanceTuning {
//...
}
```
### [Example - OSLog]
```cpp=
//import os

func reconstruectObjects() {
    guard let layerTemplateScript = layerTemplateScript else { return }
    
    //let log = OSLog(subsystem:subSystem, category:.pointsOfInterest)
    //os_signpost(.begin, log:log, name:"reconstruectObjects")
    
    if controller.getLayerTemplate().loadTemplateFromBuffer(xmlBuffer: layerTemplateScript, encode: true) {

        controller.reconstruectObjects()
                 
        for (index, object) in controller.getObjectArray().enumerated() {
            object.selectObject(_isSelected: objectSelectedStatus[index])
        }
        controller.updateTrackExpandStatus(trackExpandStatus: trackExpandStatus)
    }
    
    //os_signpost(.end, log:log, name:"reconstruectObjects")
}
```
### [Example - PerformanceTuning]
```cpp=
func reconstruectObjects() {
    guard let layerTemplateScript = layerTemplateScript else { return }
    
    //let pt = PT()
    //pt.begin(pointName: "reconstruectObjects")
    
    if controller.getLayerTemplate().loadTemplateFromBuffer(xmlBuffer: layerTemplateScript, encode: true) {

        controller.reconstruectObjects()
                 
        for (index, object) in controller.getObjectArray().enumerated() {
            object.selectObject(_isSelected: objectSelectedStatus[index])
        }
        controller.updateTrackExpandStatus(trackExpandStatus: trackExpandStatus)
    }
    
    //pt.end(pointName: "reconstruectObjects")
}
```
### [Example - AutoPerformanceTuning]
```cpp=
func reconstruectObjects() {
    guard let layerTemplateScript = layerTemplateScript else { return }
    
    //let pt = AutoPT(pointName: "reconstruectObjects")
    
    if controller.getLayerTemplate().loadTemplateFromBuffer(xmlBuffer: layerTemplateScript, encode: true) {

        controller.reconstruectObjects()
                 
        for (index, object) in controller.getObjectArray().enumerated() {
            object.selectObject(_isSelected: objectSelectedStatus[index])
        }
        controller.updateTrackExpandStatus(trackExpandStatus: trackExpandStatus)
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2020-05-28
:::info
### [SelectSlider.swift]
```cpp=
/*
enum SliderType:Int {
    case normal = 0
    case tickMark
}
*/

@IBInspectable var sliderType:Int = 1
```

![](https://imgur.com/mjMxWBJ.png)

![](https://imgur.com/M60V0wL.png)
:::

<i class="fa fa-calendar fa-lg"></i> 2020-04-30
:::info
### [extension CIImage]
```cpp=
//extension in CIImage+extension.swift
extension CIImage {
    var nsImage: NSImage? {
        let rep = NSCIImageRep(ciImage: self)
        let _nsImage = NSImage(size: rep.size)
        _nsImage.addRepresentation(rep)
        
        return _nsImage
    }
}

//sample code in TitleEffectImporter.swift
private func getThumbnail(effectID:String)->NSImage? {
    if TitleNoEffect.isNoEffect(effectID: effectID) {
        return thumbGenerator.getNoEffectImage()?.nsImage
    }
        
    //check Assets
    if let image = NSImage(named: NSImage.Name(effectID)) {
        return ImageUtility.createCIImage(from: image)?.nsImage
    }
        
    //check cache folder
    let cacheFolderURL:URL = PathUtility.getCachesDirectory().appendingPathComponent("DSPCache/Title")
    let imagePathURL:URL = cacheFolderURL.appendingPathComponent(effectID).appendingPathExtension("jpg")
        
    if PathUtility.checkIfPathExists(path: imagePathURL.path) {
        return CIImage(contentsOf: imagePathURL)?.nsImage
    }

    //generate thumbnail to cache folder
    let startPeriod:TitleDefinition.PathPeriod = TitleDefinition.PathPeriod(start: 0.0, end: 1.0)
    let endPeriod:TitleDefinition.PathPeriod = TitleDefinition.PathPeriod()
    title?.setupPath(effectID, startPeriod, "", endPeriod)
        
    if let thumbnail = thumbGenerator.createThumbnail(title: title, progress: 0.3) {
        _ = writeThumbnailToFile(cacheFolderPath: cacheFolderURL.path, exportFilePath: imagePathURL.path, _thumbnail: thumbnail)
            
        return thumbnail.nsImage
    }
        
    return nil
}
```

### [RepeatingTimer]
```cpp=
//RepeatingTimer.swift
class RepeatingTimer {
    init(seconds timeInterval: TimeInterval) {
        self.timeInterval = timeInterval
    }
    
    deinit {
        timer.setEventHandler {}
        timer.cancel()
        /*
         If the timer is suspended, calling cancel without resuming
         triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
         */
        resume()
        eventHandler = nil
    }
    
    //MARK:public
    var eventHandler: (() -> Void)?
    
    func resume() {
        if state == .resumed {
            return
        }
        state = .resumed
        timer.resume()
    }

    func suspend() {
        if state == .suspended {
            return
        }
        state = .suspended
        timer.suspend()
    }
    
    //MARK:private
    private enum State {
        case suspended
        case resumed
    }
    
    private let timeInterval: TimeInterval
    private var state: State = .suspended
    
    private lazy var timer: DispatchSourceTimer = {
        let t = DispatchSource.makeTimerSource()
        t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
        t.setEventHandler(handler: { [weak self] in
            self?.eventHandler?()
        })
        return t
    }()
}

//sample code in TitleStartEndEffectViewer.swift
class TitleStartEndEffectViewer {
    private var title:CES_Title? = nil
    private var effect:TitleEffect? = nil
    private weak var mtkView:MTKView! = nil
    private let timeInterval = 0.1
    private var timer:RepeatingTimer? = nil
    private var thumbGenerator:TitleEffectThumbGenerator = TitleEffectThumbGenerator()
    private var position:Double = 0
    
    init(_mtkView:MTKView, _title:CES_Title, _effect:TitleEffect, pageType:TitleEffectPageType) {
        title = _title
        effect = _effect
        mtkView = _mtkView
        timer = RepeatingTimer(seconds: timeInterval)

        configureTitle(pageType: pageType)
        
        if let _timer = timer {
            _timer.eventHandler = {[weak self] in
                DispatchQueue.main.async(execute: {
                    if let weakself = self {
                        weakself.mtkView.setNeedsDisplay(weakself.mtkView.bounds)
                    }
                })
            }
        }
    }
    
    deinit {
        pause()
    }
    
    func pause() {
        guard let t = timer else { return }
        t.suspend()
    }
    
    func unpause() {
        guard let t = timer else { return }
        t.resume()
    }
    
    func updateView(view: MTKView) {
        guard let title = title,
            let thumbnail = thumbGenerator.createThumbnail(title: title, progress: getProgress()) else { return }
        
        if let pixelBuffer = thumbnail.pixelBuffer {
            ImageUtility.blendWithEditingContext(inputBuffer: pixelBuffer,
                                                 outputView: view,
                                                 clearColor: bkClearColor)
        }
    }
    
    //ignore some code...
}

//sample code in TitleEffectBaseViewController.swift
extension TitleEffectBaseViewController : TitleEffectCollectionViewItemDelegate {
    func onEnterPreview(imageView: NSView?, titleEffect: TitleEffect?) {
        guard let view = imageView,
            let title = importer.getTitle(),
            let effect = titleEffect else { return }

        if TitleNoEffect.isNoEffect(effectID: effect.effectID) { return }
        if effectViewer != nil { return }

        mtkView = MTKView(frame: view.bounds, device: EditingContext.shared.device)
        mtkView?.framebufferOnly = true
        mtkView?.enableSetNeedsDisplay = true
        mtkView?.delegate = self

        view.addSubview(mtkView!)
        
        effectViewer = TitleStartEndEffectViewer(_mtkView: mtkView!, _title: title, _effect: effect, pageType: getPageType())
        effectViewer?.unpause()
    }

    func onLeavePreview(titleEffect:TitleEffect?) {
        guard let effect = titleEffect else { return }
        if TitleNoEffect.isNoEffect(effectID: effect.effectID) { return }
        
        effectViewer?.pause()
        effectViewer = nil
        mtkView?.removeFromSuperview()
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2020-03-12
:::info
### [presentAsModalWindow]
```cpp=
@objc func popupTitleDesigner(noti: Notification) {
        guard let userInfo = noti.userInfo else {return}
        guard let trackIdx: Int         = userInfo["trackIndex"] as? Int,
              let clipIdx : Int         = userInfo["clipIndex"]  as? Int else {return}
        
        guard let tlObj = EditingManager.shared.getTLObject(trackIdx, clipIdx) as? TLTitleObject else { return }
        
        let storyboard = NSStoryboard(name: NSStoryboard.Name("TitleDesigner"), bundle: nil)
        
        if let viewController = storyboard.instantiateInitialController() as? TitleDesignerViewController {
            let clipInfo = TemplateClipInfo(trackIndex: trackIdx,
                                            clipIndex: clipIdx,
                                            tlBegin: tlObj.getTLBegin(),
                                            tlDuration: tlObj.getTLDuration(),
                                            tlIndicator: EditingManager.shared.position,
                                            refScriptPath: tlObj.getUserData().scriptPath,
                                            scriptBuffEnc: tlObj.getScriptBuffer())
            viewController.initDialog(clipInfo: clipInfo)
            presentAsModalWindow(viewController)
        }
    }

/*
[enter designer]
storyboard.instantiateInitialController() 
initDialog
presentAsModalWindow
viewDidLoad
prepare
viewDidAppear
Leave function scope

[leave designer]
windowWillClose
deinit
*/
```

### [NSApplication.shared.runModal]
```cpp=
@IBAction func clickAddParticleButton(_ sender: Any) {
        let storyboard = NSStoryboard(name: NSStoryboard.Name("ImportParticleDlg"), bundle: nil)
        
        guard let windowController = storyboard.instantiateInitialController() as? NSWindowController,
            let window = windowController.window,
            let viewController = windowController.contentViewController as? ImportParticleDlgViewController else { return }
        
        if NSApplication.shared.runModal(for: window) == .OK {
            if let _controller = controller as? TitleController {
                if viewController.getScriptPath().isEmpty == false {
                    _controller.addParticle(filePath: viewController.getScriptPath(), templateAlias: viewController.getAlias())
                }
            }
        }
    }

/*
[enter designer]
viewDidLoad  (instantiate window controller)
prepare      (instantiate window controller)
initDialog
NSApplication.shared.runModal
viewDidAppear

[leave designer]
windowWillClose
NSApplication.shared.stopModal
Leave function scope 
deinit

 * /
```
:::

<i class="fa fa-calendar fa-lg"></i> 2019-09-20
:::info
### [NSSlider+extension]
```cpp=
//in NSControl
//open var intValue: Int32
//open var integerValue: Int
//open var floatValue: Float
//open var doubleValue: Double

extension NSSlider {
    var uint64Value: UInt64 {
        get {
            return UInt64(self.integerValue)
        }
        set {
            self.integerValue = Int(newValue)
        }
    }
}
```
### [Before]
```cpp=
guard let position = controller?.position() else { assert(false); return }
sliderPlayback.integerValue = Int(position)
```
### [After]
```cpp=
guard let position = controller?.position() else { assert(false); return }
sliderPlayback.uint64Value = position
```
:::
:::warning
### [Custom NotificationCenter : post part] 
### [Before]
```cpp=
extension Notification.Name {
    static let displayPanelPlayback = NSNotification.Name("displayPanelPlayback")
}

//key
struct DPKey {
    static let keyPlayStatus = "playStatus"
}

//post
var userInfo: Dictionary<String, Any> = Dictionary<String, Any>()
userInfo[DPKey.keyPlayStatus] = DPPlayStatus.play
NotificationCenter.default.post(name: .displayPanelSeekPreviewSlider, object: nil, userInfo: userInfo)

//receive
@objc private func displayPanelPlayback(_ notification:Notification){
    if let userInfo = notification.userInfo {
        if let playStatus = userInfo[DPKey.keyPlayStatus] as? DPPlayStatus{
            updatePlayback(playStatus: playStatus)
        }
    }
}
```
### [After]
```cpp=
struct dpPlaybackInfo : NotificationRepresentable{//NotificationCenter+extension.swift 
    var playStatus : DPPlayStatus
}

extension Notification.Name {
    static let displayPanelPlayback = dpPlaybackInfo.notificationName //PowerDirector.dpPlaybackInfo
}

//post
NotificationCenter.default.post(dpPlaybackInfo(playStatus: DPPlayStatus.play))

//receive
@objc private func displayPanelPlayback(_ notification:Notification){
    guard let info = dpPlaybackInfo(notification: notification) else { return }
    updatePlayback(playStatus: info.playStatus)
}
```
### [NotificationCenter+extension]
```cpp=
protocol NotificationRepresentable { }

extension NotificationRepresentable {
    static var notificationName: Notification.Name {
        return Notification.Name(String(reflecting: Self.self))
    }
    static var userInfoKey: String {
        return "UserInfo"
    }
    
    init?(notification: Notification) {
        guard let value = notification.userInfo?[Self.userInfoKey] as? Self else { return nil }
        self = value
    }
}

extension NotificationCenter {
    func post<T>(_ notificationRepresentable: T, object: Any? = nil) where T: NotificationRepresentable {
        post(name: T.notificationName, object: object, userInfo: [T.userInfoKey : notificationRepresentable])
    }
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2019-07-04
### [Memory leak case] 
```cpp=
void CTitleElegantFontSettingPage::InitTitleFontNameCombo()
{
	std::vector<std::pair<CString, BYTE>> vecFontName;
	m_pITitleModeController->EnumElegantTitleFontName(vecFontName);

	for (int nIdx=0 ; nIdx<(int)vecFontName.size() ; ++nIdx)
	{
		const CString strFontName = vecFontName[nIdx].first;
		const BYTE byCharSet = vecFontName[nIdx].second;

		const CString strFontFaceName = strFontName.Left(LF_FACESIZE-1);
        
		m_pcboFontName->AddString(strFontFaceName);
		//m_pcboFontName->InsertString(nIdx, strFontFaceName);
		
		m_mapCharSet[strFontFaceName] = byCharSet;
		std::pair<BYTE, CString> * pPairItemData = new std::pair<BYTE, CString>(byCharSet, strFontName);
		m_pcboFontName->SetItemDataPtr(nIdx, pPairItemData);
	}
}
```
### [Resolve] 
```cpp=
void CTitleElegantFontSettingPage::InitTitleFontNameCombo()
{
	std::vector<std::pair<CString, BYTE>> vecFontName;
	m_pITitleModeController->EnumElegantTitleFontName(vecFontName);

	for (int nIdx=0 ; nIdx<(int)vecFontName.size() ; ++nIdx)
	{
		const CString strFontName = vecFontName[nIdx].first;
		const BYTE byCharSet = vecFontName[nIdx].second;

		const CString strFontFaceName = strFontName.Left(LF_FACESIZE-1);
		const int nInsertIdx = m_pcboFontName->AddString(strFontFaceName);
        
		m_mapCharSet[strFontFaceName] = byCharSet;
		std::pair<BYTE, CString> * pPairItemData = new std::pair<BYTE, CString>(byCharSet, strFontName);
		m_pcboFontName->SetItemDataPtr(nInsertIdx, pPairItemData);
	}
}
```
### [note1] 
```htmlembedded=
/*skin_Title_Elegant_FontSetting.xml*/
<COMBOBOX id="IDC_ELEGANT_TITLE_COMBOBOX_FONT_TYPE" SORT="TRUE">
    //ignore
</COMBOBOX> 
```
### [note2] 
```cpp=
/*TitleElegantFontSettingPage.cpp*/
if(m_pcboFontName = (CGComboBox*)GetDlgItem(IDC_ELEGANT_TITLE_COMBOBOX_FONT_TYPE))
{
    /*
    enum COMBOBOX_STYLE
    {
	CS_NORMAL,
	CS_FONT,
	CS_FONTSTYLE // Must insert 4 items [Ref : EFONTSTYLE]
    };
    */
    m_pcboFontName->SetStyle(CGComboBox::CS_FONT);
    m_pcboFontName->SetItemHeight(0, nItemHeight);
}

/*CGComboBox.cpp*/
void CGComboListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
    //...
    if(style==CGComboBox::CS_FONT)
    {
        dcBuf.SelectObject(pOldFont);
        LOGFONT lf;
        memset(&lf, 0, sizeof(LOGFONT));
        ASSERT(m_pFont!=NULL);
        CFont* pFont=m_pFont;
        pFont->GetLogFont(&lf);

        if(szText.Left(1) == _T("@"))// && IsFontExist(szText.Mid(1))) //ignore font exist check for performance concern
            _tcscpy_s(lf.lfFaceName,szText.Mid(1));
        else
            _tcscpy_s(lf.lfFaceName,szText);

        lf.lfCharSet=(BYTE)GetItemDataPtr(lpDrawItemStruct->itemID);
        lf.lfHeight = (LONG) (int) CDpiTrans(-14,FALSE);
        font.CreateFontIndirect(&lf);
        bCreateFont=TRUE;
        pOldFont=dcBuf.SelectObject(&font);
	}
    //...
}
```
---
<i class="fa fa-calendar fa-lg"></i> 2019-05-16 
### eBug : [VDE191610-0022](https://ecl.cyberlink.com/Ebug/EbugHandle/HandleMainEbug.asp?BugCode=VDE191610-0022)

### [Root Cause] 

```cpp=
void CTitleModeBaseController::ExecuteUndoPackage(TITLE_UNDO_PACKAGE* ptupPackage)
{
	//do something...

	int nZOrder = 0;
	std::vector<TitleTimelineTrackSetting> vTrackSettings;
	if(ptupPackage->plistObject && ptupPackage->plistObject->GetCount() > 0)
	{
		SortUndoObjListByZOrder(ptupPackage->plistObject);
		
		POSITION pos = ptupPackage->plistObject->GetHeadPosition();
		while(pos)
		{
			//execute undo package
		}
	}
	
	//do something...
}
```
### Relative changeset [[82710]](http://clt-svn:8090/trac/powerdirector/changeset/82710)

```cpp=
void CTitleModeBaseController::SortUndoObjListByZOrder(CList<TITLE_UNDO_OBJECT*, TITLE_UNDO_OBJECT*>* pUndoObjList)
{
	if(!pUndoObjList)
		return;

	for(INT_PTR i = 0; i < pUndoObjList->GetCount(); i++)
	{
		for(INT_PTR j = 0; j < pUndoObjList->GetCount() - 1; j++)
		{
			TITLE_UNDO_OBJECT *pUndoObjFirst = pUndoObjList->GetAt(pUndoObjList->FindIndex(j));
			CTitleBase* pTitleBaseFirst = pUndoObjFirst->pString;
			TITLE_UNDO_OBJECT *pUndoObjSecond = pUndoObjList->GetAt(pUndoObjList->FindIndex(j + 1));
			CTitleBase* pTitleBaseSecond = pUndoObjSecond->pString;
			if(pTitleBaseFirst->GetZOrder() > pTitleBaseSecond->GetZOrder())
			{
				pUndoObjList->SetAt(pUndoObjList->FindIndex(j), pUndoObjSecond);
				pUndoObjList->SetAt(pUndoObjList->FindIndex(j + 1), pUndoObjFirst);
			}
		}
	}
}
```
```cpp=
struct TITLE_UNDO_OBJECT
{
	enum TITLE_UNDO_OBJECT_TYPE
	{
		TUOT_STRING,
		TUOT_PICTURE,
		TUOT_PARTICLE,
		TUOT_ELEGANT_TITLE,
	};

	TITLE_UNDO_OBJECT_TYPE tuotObjectType;
	union{
		CTitleTitle *pString;
		CTitlePicture* pPicture;
		CTitleParticleEditable* pParticle;
		CTitleUndoElegantTitle* pElegantTitle;
	};
};
```

#### [91705]Release : **Issue can reproduce**
![](https://imgur.com/CddhFmL.png)
#### [91705]Debug : **Issue can NOT reproduce**
![](https://imgur.com/NDuTDMR.png)

### Relative changeset [[91706]](http://clt-svn:8090/trac/powerdirector/changeset/91706)
#### [91706]Release : **Issue can NOT reproduce**
![](https://imgur.com/imcJble.png)
#### [91706]Debug : **Issue can NOT reproduce**
![](https://imgur.com/1zixKsa.png)

### [Resolve]
### changeset [[92375]](http://clt-svn:8090/trac/powerdirector/changeset/92357)

---
<i class="fa fa-calendar fa-lg"></i> 2018-11-22 
##	[BB improve]

### [2D]Producing
![](https://imgur.com/zpXl83w.png)

### [2D]Produced
![](https://imgur.com/gVK5xIM.png)

### [Online]Producing
![](https://imgur.com/C3nWj6z.png)

### [Online]Produced
![](https://imgur.com/7CPsKOb.png)

### [Colud]Producing
![](https://imgur.com/HyT2l9E.png)

### [Colud]Produced
![](https://imgur.com/128Ed3q.png)

