Create a new advanced preference called "Confirmations". Move Quit Confirmations there.
Adds a new confirmation type for closing message windows with more than 1 tab open. Fixes #12006.
This new preference has two options: always confirm, only confirm when there's unread content. Displays an alert sheet when trying to close the window.
2 * AIInterfaceControllerProtocol.h
5 * Created by Evan Schoenberg on 7/31/06.
9 #import <Adium/AIControllerProtocol.h>
11 #define Interface_ContactSelectionChanged @"Interface_ContactSelectionChanged"
14 * @brief AIContactInfoInspectorDidChangeInspectedObject notification name
16 * userinfo is an NSDictionary with keys KEY_PREVIOUS_INSPECTED_OBJECT and KEY_NEW_INSPECTED_OBJECT and objects of class AIListObject
18 #define AIContactInfoInspectorDidChangeInspectedObject @"AIContactInfoInspectorWillChangeInspectedObject"
19 #define KEY_PREVIOUS_INSPECTED_OBJECT @"PreviousInspectedObject"
20 #define KEY_NEW_INSPECTED_OBJECT @"NewInspectedObject"
22 #define Interface_SendEnteredMessage @"Interface_SendEnteredMessage"
23 #define Interface_WillSendEnteredMessage @"Interface_WillSendEnteredMessage"
24 #define Interface_DidSendEnteredMessage @"Interface_DidSendEnteredMessage"
25 #define Interface_ShouldDisplayErrorMessage @"Interface_ShouldDisplayErrorMessage"
26 #define Interface_ShouldDisplayQuestion @"Interface_ShouldDisplayQuestion"
27 #define Interface_ContactListDidBecomeMain @"Interface_ContactListDidBecomeMain"
28 #define Interface_ContactListDidResignMain @"Interface_contactListDidResignMain"
29 #define Interface_ContactListDidClose @"Interface_contactListDidClose"
30 #define Interface_TabArrangingPreferenceChanged @"Interface_TabArrangingPreferenceChanged"
31 #define AIViewDesiredSizeDidChangeNotification @"AIViewDesiredSizeDidChangeNotification"
33 #define PREF_GROUP_INTERFACE @"Interface"
34 #define KEY_TABBED_CHATTING @"Tabbed Chatting"
35 #define KEY_GROUP_CHATS_BY_GROUP @"Group Chats By Group"
36 #define KEY_SAVE_CONTAINERS @"Save Containers On Quit"
37 #define KEY_CONTAINERS @"Containers"
39 #define KEY_CL_WINDOW_LEVEL @"Window Level"
40 #define KEY_CL_HIDE @"Hide While in Background"
41 #define KEY_CL_EDGE_SLIDE @"Hide On Screen Edges"
42 #define KEY_CL_FLASH_UNVIEWED_CONTENT @"Flash Unviewed Content"
43 #define KEY_CL_ANIMATE_CHANGES @"Animate Changes"
44 #define KEY_CL_SHOW_TOOLTIPS @"Show Tooltips"
45 #define KEY_CL_SHOW_TOOLTIPS_IN_BACKGROUND @"Show Tooltips in Background"
46 #define KEY_CL_WINDOW_HAS_SHADOW @"Window Has Shadow"
48 #define PREF_GROUP_CONFIRMATIONS @"Confirmations"
49 #define KEY_CONFIRM_QUIT @"Confirm Quit"
50 #define KEY_CONFIRM_QUIT_TYPE @"Confirm Quit Type"
51 #define KEY_CONFIRM_QUIT_FT @"Suppress Quit Confirmation for File Transfers"
52 #define KEY_CONFIRM_QUIT_OPEN @"Suppress Quit Confirmation for Open Chats"
53 #define KEY_CONFIRM_QUIT_UNREAD @"Suppress Quit Confirmation for Unread Messages"
54 #define KEY_CONFIRM_MSG_CLOSE @"Confirm Message Window Close"
55 #define KEY_CONFIRM_MSG_CLOSE_TYPE @"Confirm Message Window Close Type"
58 AINormalWindowLevel = 0,
59 AIFloatingWindowLevel = 1,
60 AIDesktopWindowLevel = 2
63 //Identifiers for the various message views
65 DCStandardMessageView = 1, //webkit is not available
66 DCWebkitMessageView //Preferred message view
69 @protocol AIInterfaceComponent, AIContactListComponent, AIMessageDisplayController, AIMessageDisplayPlugin;
70 @protocol AIContactListTooltipEntry, AIFlashObserver;
72 @class AIListWindowController, AIMessageWindowController, AIMessageViewController;
74 @class AIChat, AIListObject, AIListGroup, AIContactList;
76 @protocol AIInterfaceController <AIController>
77 - (void)registerInterfaceController:(id <AIInterfaceComponent>)inController;
78 - (void)registerContactListController:(id <AIContactListComponent>)inController;
80 /*! @brief Implement handling of the reopen Apple Event.
82 * @par The reopen handler should respond by making sure that at least one of Adium's windows is visible.
84 * @par Adium.app's implementation handles this event this way:
86 * @li If there are no chat windows, shows the Contact List.
87 * @li Else, if the foremost chat window and chat tab has unviewed content, make sure it stays foremost (bringing it forward of the Contact List, if necessary).
88 * @li Else, if any chat window has unviewed content, bring foremost the chat window and chat tab with the most recent unviewed content.
89 * @li Else, if all chat windows are minimized, unminimize one of them.
90 * @li If the application is hidden, unhide it.
92 * @return A value suitable for returning from the \c NSApplication delegate method <code>applicationShouldHandleReopen:hasVisibleWindows:
93 </code>. Specifically: \c YES if AppKit should perform its usual response to the event; \c NO if AppKit should do nothing.
95 - (BOOL)handleReopenWithVisibleWindows:(BOOL)visibleWindows;
98 /*! @name Contact List */
101 * @brief Brings contact list to the front
103 - (IBAction)showContactList:(id)sender;
105 * @brief Close the contact list window
107 - (IBAction)closeContactList:(id)sender;
109 * @returns YES if contact list is visible and selected, otherwise NO
111 @property (nonatomic, readonly) BOOL contactListIsVisibleAndMain;
113 * @returns YES if contact list is visible, otherwise NO
115 @property (nonatomic, readonly) BOOL contactListIsVisible;
118 #pragma mark Detachable Contact List
120 * @brief Create a new AIListWindowController to display a given contact list group in a detached window
122 * @result Created contact list controller for detached contact list
124 - (AIListWindowController *)detachContactList:(AIContactList *)aContactList;
126 #pragma mark Messaging
128 * @brief Opens tab or window for a chat (following user's preferences)
130 * @param inChat The chat. If already open, the chat will be brought to the front.
132 - (void)openChat:(AIChat *)inChat;
135 * @brief Opens a chat in a given container at a specific index
137 * NOTE: If the chat is already open, this won't move it and instead does nothing. Perhaps it should, though :)
139 * @param inChat The chat.
140 * @param containerID The name of the container window.
141 * @param index The index within that containter window.
143 - (id)openChat:(AIChat *)inChat inContainerWithID:(NSString *)containerID atIndex:(NSUInteger)index;
146 * @brief Move a chat to a new window container
148 * NOTE: The chat must already be open. That's a bug.
150 - (void)moveChatToNewContainer:(AIChat *)inChat;
153 * @brief Close the interface for a chat
155 * @param inChat The chat
157 - (void)closeChat:(AIChat *)inChat;
160 * @brief Consolidate all open chats into a single container
162 - (void)consolidateChats;
165 * @brief Active Chat property
167 * Setter brings the tab/window for a chat to the front and sets it as active
168 * If no chat is active (a non-chat window is focued, or Adium is not focused), getter returns nil.
170 @property (nonatomic, retain) AIChat *activeChat;
173 * @brief Get the chat which was most recently active
175 * If -[self activeChat] is non-nil, this will be the same as activeChat. However, if no chat is active, so long
176 * as any chat is open, this will return the chat most recently active. If no chats are open, this will return nil.
178 @property (nonatomic, readonly) AIChat *mostRecentActiveChat;
181 * @brief Get all open chats
183 * @result The open chats. Returns an empty array if no chats are open.
185 @property (nonatomic, readonly) NSArray *openChats;
188 * @brief Get all open chats in a given container.
190 * @result The array of open chats. Returns nil if no container with containerID exists.
192 - (NSArray *)openChatsInContainerWithID:(NSString *)containerID;
195 * @brief Get an array of the containerIDs of all open containers
197 @property (nonatomic, readonly) NSArray *openContainerIDs;
200 * @brief Open a new container with a given ID and name
202 * @param containerID The container's ID. Pass nil to let Adium generate one
203 * @param containerName THe name of the container. Pass nil to not specify one.
205 * @result The newly created AIMessageWindowController
207 - (AIMessageWindowController *)openContainerWithID:(NSString *)containerID name:(NSString *)containerName;
210 * @brief Cycles to the next open chat, making it active
212 * This crosses container boundaries.
214 - (void)nextChat:(id)sender;
217 * @brief Cycles to the previous open chat, making it active
219 * This crosses container boundaries.
221 - (void)previousChat:(id)sender;
223 #pragma mark Interface plugin callbacks
225 * @brief A chat window did open: rebuild our window menu to show the new chat
227 * This should be called by the interface plugin (e.g. AIDualWindowInterfacePlugin) after a chat opens
229 * @param inChat Newly created chat
231 - (void)chatDidOpen:(AIChat *)inChat;
234 * @brief A chat has become active: update our chat closing keys and flag this chat as selected in the window menu
236 * This should be called by the interface plugin (e.g. AIDualWindowInterfacePlugin)
238 * @param inChat Chat which has become active
240 - (void)chatDidBecomeActive:(AIChat *)inChat;
243 * @brief A chat has become visible: send out a notification for components and plugins to take action
245 * This should be called by the interface plugin (e.g. AIDualWindowInterfacePlugin)
247 * @param inChat Chat that has become active
248 * @param inWindow Containing chat window
250 - (void)chatDidBecomeVisible:(AIChat *)inChat inWindow:(NSWindow *)inWindow;
253 * @brief A chat window did close: rebuild our window menu to remove the chat
255 * This should be called by the interface plugin (e.g. AIDualWindowInterfacePlugin)
257 * @param inChat Chat that closed
259 - (void)chatDidClose:(AIChat *)inChat;
262 * @brief The order of chats has changed: rebuild our window menu to reflect the new order
264 * This should be called by the interface plugin (e.g. AIDualWindowInterfacePlugin)
266 - (void)chatOrderDidChange;
268 #pragma mark Chat window access
270 * @brief Find the window currently displaying a chat
272 * @returns Window for chat otherwise if the chat is not in any window, or is not visible in any window, returns nil
274 - (NSWindow *)windowForChat:(AIChat *)inChat;
278 * @brief Find the chat active in a window
280 * If the window does not have an active chat, nil is returned
282 - (AIChat *)activeChatInWindow:(NSWindow *)window;
284 #pragma mark Interface selection
286 * @brief Get the list object currently focused in the interface
288 * We find the first responder that knows about list objects and get its selected object.
289 * This may be, for example, the contact list or a chat window.
291 @property (readonly, nonatomic) AIListObject *selectedListObject;
294 * @brief Get the list object currently selected in the contact list
296 * If multiple objects are selected, this returns the first one. If possible, use arrayOfSelectedListObjectsInContactList and
297 * handle multiple selections.
299 @property (readonly, nonatomic) AIListObject *selectedListObjectInContactList;
302 * @brief Get the list objects currently selected in the contact list
304 * @result An NSArray of selected objects
306 @property (readonly, nonatomic) NSArray *arrayOfSelectedListObjectsInContactList;
309 * @brief Get the list objects currently selected in the contact list with their groups
311 * Each entry in the NSArray is an NSDictionary of the following layout:
313 * @"ListObject" => the AIListObject
314 * @"ContainingObject" => the containing object
316 * @result An NSArray of selected objects
318 @property (readonly, nonatomic) NSArray *arrayOfSelectedListObjectsWithGroupsInContactList;
320 #pragma mark Message View
322 * @brief Register an object which can handle displaying messages
324 * See @protocol AIMessageDisplayPlugin for details. An example of such an object is the WebKit Message View plugin.
326 * @param inPlugin The object, which must conform to @protocol(AIMessageDisplayPlugin)
328 - (void)registerMessageDisplayPlugin:(id <AIMessageDisplayPlugin>)inPlugin;
331 * @brief Unregister an object previously registered with registerMessageDisplayPlugin.
333 - (void)unregisterMessageDisplayPlugin:(id <AIMessageDisplayPlugin>)inPlugin;
336 * @brief Get the AIMessageDisplayController-conforming object for a chat
338 * Every chat has exactly one. You can get it!
340 - (id <AIMessageDisplayController>)messageDisplayControllerForChat:(AIChat *)inChat;
342 #pragma mark Error Display
343 - (void)handleErrorMessage:(NSString *)inTitle withDescription:(NSString *)inDesc;
344 - (void)handleMessage:(NSString *)inTitle withDescription:(NSString *)inDesc withWindowTitle:(NSString *)inWindowTitle;
346 #pragma mark Question Display
347 - (void)displayQuestion:(NSString *)inTitle withAttributedDescription:(NSAttributedString *)inDesc withWindowTitle:(NSString *)inWindowTitle
348 defaultButton:(NSString *)inDefaultButton alternateButton:(NSString *)inAlternateButton otherButton:(NSString *)inOtherButton suppression:(NSString *)inSuppression
349 target:(id)inTarget selector:(SEL)inSelector userInfo:(id)inUserInfo;
350 - (void)displayQuestion:(NSString *)inTitle withDescription:(NSString *)inDesc withWindowTitle:(NSString *)inWindowTitle
351 defaultButton:(NSString *)inDefaultButton alternateButton:(NSString *)inAlternateButton otherButton:(NSString *)inOtherButton suppression:(NSString *)inSuppression
352 target:(id)inTarget selector:(SEL)inSelector userInfo:(id)inUserInfo;
354 #pragma mark Synchronized Flashing
355 - (void)registerFlashObserver:(id <AIFlashObserver>)inObserver;
356 - (void)unregisterFlashObserver:(id <AIFlashObserver>)inObserver;
359 #pragma mark Tooltips
361 * @brief Register a tooltip-supplying object
363 * @param inEntry The object which will be queried for information when building a tooltip. Must conform to the AIContactListTooltipEntry protocol.
364 * @param isSecondary Most tooltips are secondary; this means they are displayed in the bottom half of the tooltip which can expand as needed.
366 - (void)registerContactListTooltipEntry:(id <AIContactListTooltipEntry>)inEntry secondaryEntry:(BOOL)isSecondary;
369 * @brief Unregister a previously reigstered tooltip-supplying object
371 * @param inEntry The object to unregister
372 * @param isSecondary This should match the value passed to registerContactListTooltipEntry:secondaryEntry:
374 - (void)unregisterContactListTooltipEntry:(id <AIContactListTooltipEntry>)inEntry secondaryEntry:(BOOL)isSecondary;
377 * @brief Get an array of all primary contact list tooltip entries
379 * @result An NSArray of objects conforming to AIContactListTooltipEntry
381 - (NSArray *)contactListTooltipPrimaryEntries;
384 * @brief Get an array of all secondary contact list tooltip entries
386 * @result An NSArray of objects conforming to AIContactListTooltipEntry
388 - (NSArray *)contactListTooltipSecondaryEntries;
391 * @brief Show an object's tooltip at a given point
393 * If object is not nil, the tooltip box will be shown for that object at the given point. It is created if needed;
394 * if there is an existing object tooltip, that window is moved to the given point and reconfigured for object.
395 * This is a rich-text, information-rich tooltip, not a simple text tooltip as seen elsewhere in Mac OS X.
397 * @param object The object for which the tooltip should be shown. Pass nil to hide any existing tooltip.
398 * @param point The point in screen coordinates for the tooltip's origin. If object is nil, this value is ignored.
399 * @param inWindow The window from which the tooltip is originating. If object is nil, this value is ignored.
401 - (void)showTooltipForListObject:(AIListObject *)object atScreenPoint:(NSPoint)point onWindow:(NSWindow *)inWindow;
403 #pragma mark Window level menu
405 * @brief Build a menu of possible window levels
407 * Window levels may be, for example, 'Above other windows' or 'Normally'.
409 * @param target The target on which @selector(selectedWindowLevel:) will be called when a menu item is selected
411 - (NSMenu *)menuForWindowLevelsNotifyingTarget:(id)target;
415 //Controls a contact list view
416 @protocol AIContactListViewController <NSObject>
417 - (NSView *)contactListView;
420 //Manages contact list view controllers
421 @protocol AIContactListController <NSObject>
422 - (id <AIContactListViewController>)contactListViewController;
426 * @protocol AIMessageDisplayController
427 * @brief The message display controller is responsible for, unsurprisingly, the actual display of messages.
429 * The display controller manages a view ("messageView") which will be inserted along with other UI elements such
430 * as a text entry area into a window. The Interface Plugin knows nothing about how the AIMessageDisplayController
431 * keeps its messageView up to date, nor should it, but knows that the view will show messages.
433 * The AIMessageDisplayController is informed when the message view which is using it is closing.
435 * This is, for example, the AIWebKitMessageViewController.
437 @protocol AIMessageDisplayController <NSObject>
438 - (void)setChatContentSource:(NSString *)source;
439 - (NSString *)chatContentSource;
440 - (NSString *)contentSourceName; // Unique name for this particular style of "content source".
442 - (NSView *)messageView;
443 - (NSView *)messageScrollView;
444 - (void)messageViewIsClosing;
447 - (void)jumpToPreviousMark;
448 - (BOOL)previousMarkExists;
450 - (void)jumpToNextMark;
451 - (BOOL)nextMarkExists;
453 - (void)jumpToFocusMark;
454 - (BOOL)focusMarkExists;
457 - (void)markForFocusChange;
461 * @protocol AIMessageDisplayPlugin
462 * @brief A AIMessageDisplayPlugin provides AIMessageDisplayController objects on demand.
464 * The WebKit display plugin is one example.
466 @protocol AIMessageDisplayPlugin <NSObject, AIPlugin>
467 - (id <AIMessageDisplayController>)messageDisplayControllerForChat:(AIChat *)inChat;
470 @protocol AIContactListTooltipEntry <NSObject>
471 - (NSString *)labelForObject:(AIListObject *)inObject;
472 - (NSAttributedString *)entryForObject:(AIListObject *)inObject;
473 - (BOOL)shouldDisplayInContactInspector;
476 @protocol AIFlashObserver <NSObject>
477 - (void)flash:(int)value;
480 //Handles any attributed text entry
481 @protocol AITextEntryView
482 - (void)setAttributedString:(NSAttributedString *)inAttributedString;
483 - (void)setTypingAttributes:(NSDictionary *)attrs;
484 - (BOOL)availableForSending;
488 @protocol AIInterfaceComponent <NSObject>
489 - (void)openInterface;
490 - (void)closeInterface;
491 - (id)openChat:(AIChat *)chat inContainerWithID:(NSString *)containerID withName:(NSString *)containerName atIndex:(NSUInteger)index;
492 - (void)setActiveChat:(AIChat *)inChat;
493 - (void)moveChat:(AIChat *)chat toContainerWithID:(NSString *)containerID index:(NSUInteger)index;
494 - (void)moveChatToNewContainer:(AIChat *)inChat;
495 - (void)closeChat:(AIChat *)chat;
496 - (AIMessageWindowController *)openContainerWithID:(NSString *)containerID name:(NSString *)containerName;
498 * @brief Return an array of NSDictionary objects for all open containers with associated information
500 * The returned array has zero or more NSDictionary objects with the following information for each container
502 * @"ID" NSString of the containerID
503 * @"Frame" NSString of the window's [NSWindow frame]
504 * @"Content" NSArray of the AIChat objects within that container
505 * @"ActiveChat" AIChat that is currently active
506 * @"Name" NSString of the container's name
508 @property (readonly, nonatomic) NSArray *openContainersAndChats;
509 @property (readonly, nonatomic) NSArray *openContainerIDs;
510 @property (readonly, nonatomic) NSArray *openChats;
511 - (NSArray *)openChatsInContainerWithID:(NSString *)containerID;
512 - (NSString *)containerIDForChat:(AIChat *)chat;
513 - (NSWindow *)windowForChat:(AIChat *)chat;
514 - (AIChat *)activeChatInWindow:(NSWindow *)window;
518 * @protocol AIInterfaceContainer
519 * @brief This protocol is for a general interface element such as the contact list or the container of a chat
521 @protocol AIInterfaceContainer <NSObject>
522 - (void)makeActive:(id)sender; //Make the container active/front
523 - (void)close:(id)sender; //Close the container
527 * @protocol AIChatViewController
528 * @brief A AIChatViewController manages everything pertaining to a chat's view area
530 * It will manage this view, text input, and any associated controls.
532 @protocol AIChatViewController <NSObject>
534 - (NSScrollView *)messagesScrollView;
538 * @brief Show/hide the user list for a group chat.
539 * Does nothing if not in a group chat.
541 - (void)toggleUserList;
544 * @brief Is the user list visible for a group chat?
546 - (BOOL)userListVisible;
549 * @brief Swap the side the user list is on for a group chat.
550 * Does nothing if not in a group chat.
552 - (void)toggleUserListSide;
556 * @protocol AIChatContainer
557 * @brief This protocol is for an object which displays a single chat (e.g. a tab in a chat window)
559 @protocol AIChatContainer <AIInterfaceContainer>
561 * @brief Get the window controller which holds this AIChatContainer
563 @property (readonly, nonatomic, retain) AIMessageWindowController *windowController;
566 * @brief Get the view controller for this AIChatContainer
568 @property (readonly, nonatomic) id <AIChatViewController> chatViewController;
571 * @brief Get the message view controller for this AIChatContainer
573 @property (readonly, nonatomic) AIMessageViewController *messageViewController;
576 @protocol AIContactListComponent <NSObject>
578 * @brief Show the contact list window and bring Adium to the front
580 - (void)showContactListAndBringToFront:(BOOL)bringToFront;
581 - (BOOL)contactListIsVisibleAndMain;
582 - (BOOL)contactListIsVisible;
583 - (void)closeContactList;
586 @protocol AIMultiContactListComponent <AIContactListComponent>
587 - (id)detachContactList:(AIContactList *)contactList;
588 - (void)nextDetachedContactList;
589 - (void)previousDetachedContactList;
592 //Custom printing informal protocol
593 @interface NSObject (AdiumPrinting)
594 - (void)adiumPrint:(id)sender;
595 - (BOOL)validatePrintMenuItem:(NSMenuItem *)menuItem;
598 @interface NSWindowController (AdiumBorderlessWindowClosing)
599 - (BOOL)windowPermitsClose;