Commit 89edc6ad authored by Alexandre Lision's avatar Alexandre Lision

conference: first implementation

This commit adds conferences management

- create conference by joining two ongoing calls/conferences
- create conference by calling a new peer and joining him to the ongoing
call/conference
- hanging up conferences

This commit relies on CallModel and RecenModel selection models
interactions to update call view and Smartlist row selected.

Change-Id: Ib9f6b9b870e66c8ccfb5115ad6fdb592c625eb58
Tuleap: #114
parent 01e6a1fb
......@@ -188,6 +188,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_call_received.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_call_missed.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_call_made.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_transfer.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_add_participant.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_merge_calls.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ancrage.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/audio.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_person_add.png
......
......@@ -23,7 +23,7 @@
enum class BrokerMode {
TRANSFER = 0,
//CONFERENCE
CONFERENCE,
};
- (instancetype)initWithMode:(BrokerMode) m;
......
......@@ -23,6 +23,7 @@
#import <QItemSelectionModel>
#import <QtMacExtras/qmacfunctions.h>
#import <QPixmap>
#import <QMimeData>
//LRC
#import <recentmodel.h>
......@@ -103,7 +104,13 @@ NSInteger const TXT_BUTTON_TAG = 500;
[_smartView bind:@"sortDescriptors" toObject:_treeController withKeyPath:@"sortDescriptors" options:nil];
[_smartView bind:@"selectionIndexPaths" toObject:_treeController withKeyPath:@"selectionIndexPaths" options:nil];
[_smartView setTarget:self];
[_smartView setDoubleAction:@selector(placeTransfer:)];
if ([self mode] == BrokerMode::TRANSFER) {
[_smartView setDoubleAction:@selector(placeTransfer:)];
} else {
[_smartView setDoubleAction:@selector(addParticipant:)];
}
}
// -------------------------------------------------------------------------------
......@@ -145,6 +152,60 @@ NSInteger const TXT_BUTTON_TAG = 500;
CallModel::instance().transfer(current, number);
}
// -------------------------------------------------------------------------------
// place a call to the future participant on click on Person or ContactMethod
// -------------------------------------------------------------------------------
- (void)addParticipant:(id)sender
{
auto current = CallModel::instance().selectedCall();
if (!current || [_treeController selectedNodes].count == 0)
return;
QModelIndex qIdx = [_treeController toQIdx:[_treeController selectedNodes][0]];
auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(_recentFilterModel->mapToSource(qIdx));
auto participant = RecentModel::instance().getActiveCall(originIdx);
if (participant) { //join this call with the current one
QModelIndexList source_list;
source_list << CallModel::instance().getIndex(current);
auto idx_call_dest = CallModel::instance().getIndex(participant);
auto mimeData = CallModel::instance().mimeData(source_list);
auto action = Call::DropAction::Conference;
mimeData->setProperty("dropAction", action);
if (CallModel::instance().dropMimeData(mimeData, Qt::MoveAction, idx_call_dest.row(), idx_call_dest.column(), idx_call_dest.parent())) {
NSLog(@"OK");
} else {
NSLog(@"could not drop mime data");
}
return;
}
auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
if (contactmethods.size() > 0) { // Before calling check if we properly extracted at least one contact method
auto call = CallModel::instance().dialingCall(contactmethods.first());
call->setParentCall(current);
call << Call::Action::ACCEPT;
CallModel::instance().selectCall(call);
}
}
// -------------------------------------------------------------------------------
// place a call to the future participant with entered URI
// -------------------------------------------------------------------------------
- (void) addParticipantFromUri:(NSString*) uri
{
auto current = CallModel::instance().selectedCall();
if (!current)
return;
auto number = PhoneDirectoryModel::instance().getNumber(QString::fromNSString(uri));
auto dialing = CallModel::instance().dialingCall(number);
dialing->setParentCall(current);
dialing << Call::Action::ACCEPT;
CallModel::instance().selectCall(dialing);
}
#pragma mark - NSOutlineViewDelegate methods
// -------------------------------------------------------------------------------
......@@ -216,7 +277,12 @@ NSInteger const TXT_BUTTON_TAG = 500;
{
if (commandSelector == @selector(insertNewline:)) {
if([fieldEditor.textStorage.string isNotEqualTo:@""]) {
[self transferTo:fieldEditor.textStorage.string];
if ([self mode] == BrokerMode::TRANSFER) {
[self transferTo:fieldEditor.textStorage.string];
} else {
[self addParticipantFromUri:fieldEditor.textStorage.string];
}
return YES;
}
}
......
......@@ -22,7 +22,9 @@
#import <call.h>
#import <callmodel.h>
#import <recentmodel.h>
#import <useractionmodel.h>
#import <QMimeData>
#import <contactmethod.h>
#import <qabstractitemmodel.h>
#import <QItemSelectionModel>
......@@ -52,8 +54,14 @@
@interface CurrentCallVC () <NSPopoverDelegate, ContactLinkedDelegate>
// Header info
@property (unsafe_unretained) IBOutlet NSView* headerContainer;
@property (unsafe_unretained) IBOutlet NSTextField* personLabel;
@property (unsafe_unretained) IBOutlet NSTextField* stateLabel;
@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
// Call Controls
@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
@property (unsafe_unretained) IBOutlet NSButton* holdOnOffButton;
@property (unsafe_unretained) IBOutlet NSButton* hangUpButton;
@property (unsafe_unretained) IBOutlet NSButton* recordOnOffButton;
......@@ -62,17 +70,19 @@
@property (unsafe_unretained) IBOutlet NSButton* muteVideoButton;
@property (unsafe_unretained) IBOutlet NSButton* addContactButton;
@property (unsafe_unretained) IBOutlet NSButton* transferButton;
@property (unsafe_unretained) IBOutlet NSView* headerContainer;
@property (unsafe_unretained) IBOutlet NSButton* addParticipantButton;
@property (unsafe_unretained) IBOutlet NSButton* chatButton;
@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator;
@property (unsafe_unretained) IBOutlet NSTextField* timeSpentLabel;
@property (unsafe_unretained) IBOutlet NSView* controlsPanel;
// Join call panel
@property (unsafe_unretained) IBOutlet NSView* joinPanel;
@property (unsafe_unretained) IBOutlet NSButton* mergeCallsButton;
@property (unsafe_unretained) IBOutlet NSSplitView* splitView;
@property (unsafe_unretained) IBOutlet NSButton* chatButton;
@property (strong) NSPopover* addToContactPopover;
@property (strong) NSPopover* transferPopoverVC;
@property (strong) NSPopover* brokerPopoverVC;
@property (strong) IBOutlet ChatVC* chatVC;
@property QHash<int, NSButton*> actionHash;
......@@ -93,7 +103,7 @@
@implementation CurrentCallVC
@synthesize personLabel, actionHash, stateLabel, holdOnOffButton, hangUpButton,
recordOnOffButton, pickUpButton, chatButton, transferButton, timeSpentLabel,
recordOnOffButton, pickUpButton, chatButton, transferButton, addParticipantButton, timeSpentLabel,
muteVideoButton, muteAudioButton, controlsPanel, headerContainer, videoView,
videoLayer, previewLayer, previewView, splitView, loadingIndicator;
......@@ -124,6 +134,8 @@
if (!callIdx.isValid()) {
return;
}
auto current = CallModel::instance().getCall(callIdx);
[personLabel setStringValue:callIdx.data(Qt::DisplayRole).toString().toNSString()];
[timeSpentLabel setStringValue:callIdx.data((int)Call::Role::Length).toString().toNSString()];
......@@ -135,9 +147,10 @@
// Default values for this views
[loadingIndicator setHidden:YES];
[self.chatButton setHidden:YES];
[videoView setShouldAcceptInteractions:NO];
[self.controlsPanel setHidden:current->hasParentCall()];
[self.joinPanel setHidden:!current->hasParentCall()];
[stateLabel setStringValue:callIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
switch (state) {
......@@ -154,9 +167,17 @@
break;
case Call::State::RINGING:
break;
case Call::State::CONFERENCE:
[videoView setShouldAcceptInteractions:YES];
[self.chatButton setHidden:NO];
[self.addParticipantButton setHidden:NO];
[self.transferButton setHidden:YES];
break;
case Call::State::CURRENT:
[videoView setShouldAcceptInteractions:YES];
[self.chatButton setHidden:NO];
[self.addParticipantButton setHidden:NO];
[self.transferButton setHidden:NO];
break;
case Call::State::HOLD:
break;
......@@ -177,18 +198,12 @@
[self.view setWantsLayer:YES];
[self.view setLayer:[CALayer layer]];
[controlsPanel setWantsLayer:YES];
[controlsPanel setLayer:[CALayer layer]];
[controlsPanel.layer setZPosition:2.0];
[controlsPanel.layer setBackgroundColor:[NSColor whiteColor].CGColor];
actionHash[ (int)UserActionModel::Action::ACCEPT] = pickUpButton;
actionHash[ (int)UserActionModel::Action::HOLD ] = holdOnOffButton;
actionHash[ (int)UserActionModel::Action::RECORD] = recordOnOffButton;
actionHash[ (int)UserActionModel::Action::HANGUP] = hangUpButton;
actionHash[ (int)UserActionModel::Action::MUTE_AUDIO] = muteAudioButton;
actionHash[ (int)UserActionModel::Action::MUTE_VIDEO] = muteVideoButton;
actionHash[ (int)UserActionModel::Action::SERVER_TRANSFER] = transferButton;
videoLayer = [CALayer layer];
[videoView setWantsLayer:YES];
......@@ -225,15 +240,17 @@
- (void) connect
{
QObject::connect(CallModel::instance().selectionModel(),
QObject::connect(RecentModel::instance().selectionModel(),
&QItemSelectionModel::currentChanged,
[=](const QModelIndex &current, const QModelIndex &previous) {
if(!current.isValid()) {
auto call = RecentModel::instance().getActiveCall(current);
if(!current.isValid() || !call) {
[self animateOut];
return;
}
auto call = CallModel::instance().getCall(current);
CallModel::instance().selectCall(call);
if (call->state() == Call::State::HOLD) {
call << Call::Action::HOLD;
}
......@@ -276,6 +293,7 @@
{
QModelIndex idx = CallModel::instance().selectionModel()->currentIndex();
Call* call = CallModel::instance().getCall(idx);
QObject::disconnect(self.videoStarted);
self.videoStarted = QObject::connect(call,
&Call::videoStarted,
[=](Video::Renderer* renderer) {
......@@ -457,10 +475,15 @@
[videoView.layer setContents:nil];
[previewView.layer setContents:nil];
[self.transferPopoverVC performClose:self];
[_brokerPopoverVC performClose:self];
[self.addToContactPopover performClose:self];
[self.chatButton setHidden:YES];
[self.addParticipantButton setHidden:YES];
[self.transferButton setHidden:YES];
[self.chatButton setState:NSOffState];
[self.mergeCallsButton setState:NSOffState];
[self collapseRightView];
}
......@@ -469,9 +492,6 @@
NSLog(@"animateOut");
if(self.view.frame.origin.x < 0) {
NSLog(@"Already hidden");
if (CallModel::instance().selectionModel()->currentIndex().isValid()) {
[self animateIn];
}
return;
}
......@@ -487,7 +507,7 @@
[self.view setHidden:YES];
// first make sure everything is disconnected
[self cleanUp];
if (CallModel::instance().selectionModel()->currentIndex().isValid()) {
if (RecentModel::instance().getActiveCall(RecentModel::instance().selectionModel()->currentIndex())) {
[self animateIn];
}
}];
......@@ -602,30 +622,57 @@
}
- (IBAction)toggleTransferView:(id)sender {
if (self.transferPopoverVC != nullptr) {
[self.transferPopoverVC performClose:self];
self.transferPopoverVC = NULL;
if (_brokerPopoverVC != nullptr) {
[_brokerPopoverVC performClose:self];
_brokerPopoverVC = NULL;
[self.transferButton setState:NSOffState];
} else {
auto* transferVC = [[BrokerVC alloc] initWithMode:BrokerMode::TRANSFER];
self.transferPopoverVC = [[NSPopover alloc] init];
[self.transferPopoverVC setContentSize:transferVC.view.frame.size];
[self.transferPopoverVC setContentViewController:transferVC];
[self.transferPopoverVC setAnimates:YES];
[self.transferPopoverVC setBehavior:NSPopoverBehaviorTransient];
[self.transferPopoverVC setDelegate:self];
[self.transferPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::TRANSFER];
_brokerPopoverVC = [[NSPopover alloc] init];
[_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
[_brokerPopoverVC setContentViewController:brokerVC];
[_brokerPopoverVC setAnimates:YES];
[_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
[_brokerPopoverVC setDelegate:self];
[_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
[videoView setCallDelegate:nil];
}
}
- (IBAction)toggleAddParticipantView:(id)sender {
if (_brokerPopoverVC != nullptr) {
[_brokerPopoverVC performClose:self];
_brokerPopoverVC = NULL;
[self.addParticipantButton setState:NSOffState];
} else {
auto* brokerVC = [[BrokerVC alloc] initWithMode:BrokerMode::CONFERENCE];
_brokerPopoverVC = [[NSPopover alloc] init];
[_brokerPopoverVC setContentSize:brokerVC.view.frame.size];
[_brokerPopoverVC setContentViewController:brokerVC];
[_brokerPopoverVC setAnimates:YES];
[_brokerPopoverVC setBehavior:NSPopoverBehaviorTransient];
[_brokerPopoverVC setDelegate:self];
[_brokerPopoverVC showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
[videoView setCallDelegate:nil];
}
}
/**
* Merge current call with its parent call
*/
- (IBAction)mergeCalls:(id)sender
{
auto current = CallModel::instance().selectedCall();
current->joinToParent();
}
#pragma mark - NSPopOverDelegate
- (void)popoverWillClose:(NSNotification *)notification
{
if (self.transferPopoverVC != nullptr) {
[self.transferPopoverVC performClose:self];
self.transferPopoverVC = NULL;
if (_brokerPopoverVC != nullptr) {
[_brokerPopoverVC performClose:self];
_brokerPopoverVC = NULL;
}
if (self.addToContactPopover != nullptr) {
......@@ -635,6 +682,7 @@
[self.addContactButton setState:NSOffState];
[self.transferButton setState:NSOffState];
[self.addParticipantButton setState:NSOffState];
}
- (void)popoverDidClose:(NSNotification *)notification
......
......@@ -98,9 +98,10 @@ NSInteger const DETAILS_TAG = 300;
QVariant var = historyProxyModel->data(qIdx, (int)Call::Role::ContactMethod);
ContactMethod* m = qvariant_cast<ContactMethod*>(var);
if(m){
Call* c = CallModel::instance().dialingCall();
auto c = CallModel::instance().dialingCall();
c->setPeerContactMethod(m);
c << Call::Action::ACCEPT;
CallModel::instance().selectCall(c);
}
}
}
......
......@@ -122,6 +122,7 @@ NSInteger const CALL_BUTTON_TAG = 400;
Call* c = CallModel::instance().dialingCall();
c->setPeerContactMethod(m);
c << Call::Action::ACCEPT;
CallModel::instance().selectCall(c);
}
}
}
......
......@@ -28,5 +28,6 @@
- (id) initWithQModel:(QAbstractItemModel*) model;
- (QModelIndex) toQIdx:(NSTreeNode*) node;
- (QModelIndex) indexPathtoQIdx:(NSIndexPath*) path;
- (void) setSelectionQModelIndex:(QModelIndex) qIdx;
@end
......@@ -142,6 +142,12 @@
}
}
- (void) setSelectionQModelIndex:(QModelIndex) qIdx
{
NSIndexPath* path = [self qIdxToNSIndexPath:qIdx];
[self setSelectionIndexPath:path];
}
- (void)connect
{
QObject::connect(self->privateQModel,
......
......@@ -79,6 +79,7 @@ NSInteger const TXT_BUTTON_TAG = 500;
[smartView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
[smartView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
[smartView setTarget:self];
[smartView setAction:@selector(selectRow:)];
[smartView setDoubleAction:@selector(placeCall:)];
[smartView setContextMenuDelegate:self];
......@@ -94,6 +95,24 @@ NSInteger const TXT_BUTTON_TAG = 500;
}
});
QObject::connect(RecentModel::instance().selectionModel(),
&QItemSelectionModel::currentChanged,
[=](const QModelIndex &current, const QModelIndex &previous) {
if(!current.isValid())
return;
auto proxyIdx = RecentModel::instance().peopleProxy()->mapFromSource(current);
if (proxyIdx.isValid()) {
[treeController setSelectionQModelIndex:proxyIdx];
[showContactsButton setState:NO];
isShowingContacts = NO;
[showHistoryButton setState:NO];
[tabbar selectTabViewItemAtIndex:0];
[smartView scrollRowToVisible:proxyIdx.row()];
}
});
[self.view setWantsLayer:YES];
[self.view setLayer:[CALayer layer]];
[self.view.layer setBackgroundColor:[NSColor whiteColor].CGColor];
......@@ -103,6 +122,13 @@ NSInteger const TXT_BUTTON_TAG = 500;
[searchField.layer setBackgroundColor:[NSColor colorWithCalibratedRed:0.949 green:0.949 blue:0.949 alpha:0.9].CGColor];
}
-(void) selectRow:(id)sender
{
auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
auto proxyIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
RecentModel::instance().selectionModel()->setCurrentIndex(proxyIdx, QItemSelectionModel::ClearAndSelect);
}
- (void)placeCall:(id)sender
{
QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
......@@ -131,12 +157,10 @@ NSInteger const TXT_BUTTON_TAG = 500;
// Before calling check if we properly extracted a contact method and that
// there is NOT already an ongoing call for this index (e.g: no children for this node)
if(m && !RecentModel::instance().peopleProxy()->index(0, 0, qIdx).isValid()){
Call* c = CallModel::instance().dialingCall();
auto c = CallModel::instance().dialingCall();
c->setPeerContactMethod(m);
c << Call::Action::ACCEPT;
[smartView deselectAll:nil];
[smartView selectRowIndexes:[[NSIndexSet alloc] initWithIndex:0] byExtendingSelection:NO];
CallModel::instance().selectCall(c);
}
}
......@@ -184,27 +208,15 @@ NSInteger const TXT_BUTTON_TAG = 500;
return NO;
}
// -------------------------------------------------------------------------------
// outlineViewSelectionDidChange:notification
// -------------------------------------------------------------------------------
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
if ([treeController selectedNodes].count <= 0) {
CallModel::instance().selectionModel()->clearCurrentIndex();
RecentModel::instance().selectionModel()->clearCurrentIndex();
return;
}
QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
// ask the tree controller for the current selection
if (auto selected = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(qIdx))) {
CallModel::instance().selectCall(selected);
} else if (auto selected = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(qIdx.parent()))){
CallModel::instance().selectCall(selected);
} else {
CallModel::instance().selectionModel()->clearCurrentIndex();
}
}
/* View Based OutlineView: See the delegate method -tableView:viewForTableColumn:row: in NSTableView.
......
/* Class = "NSTextFieldCell"; title = "Display Role"; ObjectID = "1Rl-Yq-z7r"; */
"1Rl-Yq-z7r.title" = "Display Role";
/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "3lU-Go-v0h"; */
"3lU-Go-v0h.title" = "Text Cell";
/* Class = "NSSearchFieldCell"; placeholderString = "Search or enter number"; ObjectID = "5vh-ub-Dsq"; */
"5vh-ub-Dsq.placeholderString" = "Search or enter number";
/* Class = "NSTextFieldCell"; title = "details"; ObjectID = "sQW-4V-hwv"; */
"sQW-4V-hwv.title" = "details";
/* Class = "NSButton"; ibShadowedToolTip = "Join call"; ObjectID = "9e8-ji-QId"; */
"9e8-ji-QId.ibShadowedToolTip" = "Join call";
/* Class = "NSButton"; ibShadowedToolTip = "Transfer"; ObjectID = "ChW-kg-Sja"; */
"ChW-kg-Sja.ibShadowedToolTip" = "Transfer";
/* Class = "NSTextFieldCell"; title = "URI"; ObjectID = "Dqv-um-UUk"; */
"Dqv-um-UUk.title" = "URI";
/* Class = "NSTextFieldCell"; title = "details"; ObjectID = "FHf-0w-WGz"; */
"FHf-0w-WGz.title" = "details";
/* Class = "NSButtonCell"; title = "Answer"; ObjectID = "KPG-pB-gPm"; */
"KPG-pB-gPm.title" = "Answer";
/* Class = "NSTextFieldCell"; title = "Display Role"; ObjectID = "FhU-gw-okq"; */
"FhU-gw-okq.title" = "Display Role";
/* Class = "NSButton"; ibShadowedToolTip = "Hang up"; ObjectID = "Kjq-iM-NBL"; */
"Kjq-iM-NBL.ibShadowedToolTip" = "Hang up";
/* Class = "NSSearchFieldCell"; placeholderString = "Search or enter number"; ObjectID = "GeP-AW-8mu"; */
"GeP-AW-8mu.placeholderString" = "Search or enter number";
/* Class = "NSButton"; ibShadowedToolTip = "Mute Video"; ObjectID = "LVS-yZ-98V"; */
"LVS-yZ-98V.ibShadowedToolTip" = "Mute Video";
/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "GjO-zY-hdp"; */
"GjO-zY-hdp.title" = "Text Cell";
/* Class = "NSButton"; ibShadowedToolTip = "Hold"; ObjectID = "anb-Y8-JQi"; */
"anb-Y8-JQi.ibShadowedToolTip" = "Hold";
/* Class = "NSButtonCell"; title = "Answer"; ObjectID = "KPG-pB-gPm"; */
"KPG-pB-gPm.title" = "Answer";
/* Class = "NSButton"; ibShadowedToolTip = "Toggle chat"; ObjectID = "fmp-x4-Pef"; */
"fmp-x4-Pef.ibShadowedToolTip" = "Toggle chat";
/* Class = "NSButton"; ibShadowedToolTip = "Add participant"; ObjectID = "kIZ-mf-moM"; */
"kIZ-mf-moM.ibShadowedToolTip" = "Add participant";
/* Class = "NSButtonCell"; title = "Button"; ObjectID = "kxH-yM-TLd"; */
"kxH-yM-TLd.title" = "Button";
/* Class = "NSButton"; ibShadowedToolTip = "Hang up"; ObjectID = "mc3-HV-hek"; */
"mc3-HV-hek.ibShadowedToolTip" = "Hang up";
/* Class = "NSButton"; ibShadowedToolTip = "Record"; ObjectID = "oRa-pS-HN2"; */
"oRa-pS-HN2.ibShadowedToolTip" = "Record";
/* Class = "NSTextFieldCell"; title = "Person name"; ObjectID = "osk-LS-0Qg"; */
"osk-LS-0Qg.title" = "Person name";
/* Class = "NSButton"; ibShadowedToolTip = "Pick up"; ObjectID = "qgD-3D-nD5"; */
"qgD-3D-nD5.ibShadowedToolTip" = "Pick up";
/* Class = "NSButton"; ibShadowedToolTip = "Mute Audio"; ObjectID = "tQl-cT-0Lb"; */
"tQl-cT-0Lb.ibShadowedToolTip" = "Mute Audio";
/* Class = "NSTextFieldCell"; title = "State"; ObjectID = "ugy-uK-901"; */
"ugy-uK-901.title" = "State";
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment