Page 1 of 2

EventClose on Dock

Posted: Fri Aug 29, 2014 9:50 pm
by spacebuddy
If I minimize my application, then right click on the icon on the dock, the one with the little light under it, then select quit no event is fired? :shock:

How do I detect this event, I don't want anyone clicking on the dock icon and just closing my application without a warning :D

Re: EventClose on Dock

Posted: Fri Aug 29, 2014 10:32 pm
by Polo
This event is #pb_menu_quit (or something like that).

Re: EventClose on Dock

Posted: Fri Aug 29, 2014 10:50 pm
by spacebuddy
Polo wrote:This event is #pb_menu_quit (or something like that).
No, that is not the one. That is when you click on the top menu and select "Quit". I am talking about your application being minimized
on the dock, then right clicking the icon and selecting "Quit". :D

Re: EventClose on Dock

Posted: Sat Aug 30, 2014 2:28 am
by Polo
From what I remember it's the same event, did you try?

Re: EventClose on Dock

Posted: Thu Sep 04, 2014 9:12 pm
by Shardik
Unfortunately #PB_Menu_Quit doesn't work in detecting the selection of "Quit" after a right click onto the application's minimized dock icon. It's a little bit more complicated: you have to define a callback for the NSApplicationDelegate method applicationShouldTerminate: and then you are even able to ask the user for a confirmation to terminate the application. Without this workaround your program will terminate with a debugger error message. Therefore I have already posted this bug report.

I have tested the following code with MacOS X 10.6.8 (Snow Leopard) with PB 5.30 x86 and x64 in both ASCII and Unicode mode:

Code: Select all

EnableExplicit

ImportC ""
  sel_registerName(MethodName.S)
  class_addMethod(Class.I, Selector.I, Implementation.I, Types.S)
EndImport

ProcedureC AppShouldTerminate(Object.I, Selector.I, Sender.I)
  If MessageRequester("Confirmation", "Do you want to terminate the application?",
    #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
    End
  Else
    ProcedureReturn #NO
  EndIf
EndProcedure

Procedure.S ConvertToUTF8(String.S)
  Protected UTF8String.S = Space(StringByteLength(String))
  PokeS(@UTF8String, String, -1, #PB_UTF8)
  ProcedureReturn UTF8String
EndProcedure

Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
  "NSApplication sharedApplication"), "delegate")

OpenWindow(0, 270, 100, 200, 170, "Test", #PB_Window_MinimizeGadget)

class_addMethod(CocoaMessage(0, AppDelegate, "class"),
  sel_registerName(ConvertToUTF8("applicationShouldTerminate:")),
  @AppShouldTerminate(), "v@:@")

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow

Re: EventClose on Dock

Posted: Tue Sep 23, 2014 3:52 am
by kenmo
THANK YOU SHARDIK

I have not tested your code yet, but this looks like exactly what I need.

(My program isn't catching the "Quit" dock event, so it doesn't exit properly, which later causes problems the next time it runs!)

Re: EventClose on Dock

Posted: Fri Aug 30, 2019 10:03 am
by Rinzwind
The code of Shardik doesn't seem to work anymore?

Just interested in getting the applicationShouldTerminate and applicationWillTerminate notificatoin.

Re: EventClose on Dock

Posted: Fri Aug 30, 2019 11:53 am
by wilbert
class_addMethod only adds a method if it doesn't exist.
It appears PureBasic itself started using applicationShouldTerminate: which wasn't the case at the time this code was posted.
One way would be to use class_replaceMethod and call the previous implementation of applicationShouldTerminate: from within the callback.
Something like this.

Code: Select all

EnableExplicit

Prototype Proto_AppShouldTerminate(Object, Selector, Sender)
DeclareC AppShouldTerminate(Object, Selector, Sender)

Global AppDelegate, AppShouldTerminate_.Proto_AppShouldTerminate

AppDelegate = CocoaMessage(0, CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
AppShouldTerminate_ = class_replaceMethod_(CocoaMessage(0, AppDelegate, "class"),
                                           sel_registerName_("applicationShouldTerminate:"), 
                                           @AppShouldTerminate(), "v@:@")

ProcedureC AppShouldTerminate(Object, Selector, Sender)
  If AppShouldTerminate_
    AppShouldTerminate_(Object, Selector, Sender)
  EndIf
  If MessageRequester("Confirmation", "Do you want to terminate the application?",
                      #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
    End
  Else
    ProcedureReturn #NO
  EndIf
EndProcedure


OpenWindow(0, 270, 100, 200, 170, "Test", #PB_Window_MinimizeGadget)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
If you only need a notification, you could probably also use NSNotificationCenter and observe for NSApplicationWillTerminateNotification .

Re: EventClose on Dock

Posted: Fri Aug 30, 2019 12:19 pm
by Rinzwind
And thanks again mr iGuru. Is there anything you can't figure out irt macPB? 8)

Re: EventClose on Dock

Posted: Fri Aug 30, 2019 12:31 pm
by wilbert
Rinzwind wrote:Is there anything you can't figure out irt macPB? 8)
Of course :)
But I like macOS and do know a bit about it.

I'm sure Shardik would also have been able to figure this out since he posted the original code and knows a lot about macOS.

Re: EventClose on Dock

Posted: Wed Sep 04, 2019 12:28 pm
by Rinzwind
Hmm additional unclarity...
"For Cocoa applications, termination is partly handled by the Application Kit, which calls the applicationShouldTerminate: delegate method. To abort the termination sequence, implement this method and return NSTerminateCancel; otherwise, termination of your application continues normally."
"NSTerminateLater = 2
It may be OK to proceed with termination later. Returning this value causes Cocoa to run the run loop in the NSModalPanelRunLoopMode until your app subsequently calls replyToApplicationShouldTerminate: with the value YES or NO. This return value is for delegates that need to provide document modal alerts (sheets) in order to decide whether to quit.
"

If I return NSTerminateLater the main event loop is indeed not called anymore... a new event loop is started it seems. I have no clue how to handle that bit NSModalPanelRunLoopMode. I want to just continue with the main loop and call replyToApplicationShouldTerminate whenever convenient...

Re: EventClose on Dock

Posted: Wed Sep 04, 2019 1:55 pm
by wilbert
Rinzwind wrote:f I return NSTerminateLater the main event loop is indeed not called anymore... a new event loop is started it seems. I have no clue how to handle that bit NSModalPanelRunLoopMode. I want to just continue with the main loop and call replyToApplicationShouldTerminate whenever convenient...
I doubt if what you want is possible this way.
Like what you quoted already mentions, this is intended for something like the PureBasic MessageRequester to get confirmation if you want the app to quit or not.
If you want to continue with the main loop, just cancel the quit. You can set a flag or post an event and act on that from the main loop.

Re: EventClose on Dock

Posted: Wed Sep 04, 2019 2:14 pm
by Rinzwind
If I return #NSTerminateNow the application is instantly killed. If I return #NSTerminateCancel (0) the OS dialog app prevents shutdown is shown and shutdown is cancelled. With #NSTerminateLater the app becomes unresponsive. Hmm

Re: EventClose on Dock

Posted: Wed Sep 04, 2019 2:48 pm
by Rinzwind
Any way to at least show some moving gui like progressbar in that “sheet” runmode?

Re: EventClose on Dock

Posted: Wed Sep 04, 2019 3:07 pm
by wilbert
Is the event method I suggested not working for you ?

Code: Select all

Enumeration #PB_Event_FirstCustomValue
  #Event_TerminateRequested
EndEnumeration
  
Prototype Proto_AppShouldTerminate(Object, Selector, Sender)
DeclareC AppShouldTerminate(Object, Selector, Sender)

Global AppDelegate, AppShouldTerminate_.Proto_AppShouldTerminate

AppDelegate = CocoaMessage(0, CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
AppShouldTerminate_ = class_replaceMethod_(CocoaMessage(0, AppDelegate, "class"),
                                           sel_registerName_("applicationShouldTerminate:"), 
                                           @AppShouldTerminate(), "v@:@")

ProcedureC AppShouldTerminate(Object, Selector, Sender)
  PostEvent(#Event_TerminateRequested)
  If AppShouldTerminate_
    ProcedureReturn AppShouldTerminate_(Object, Selector, Sender)
  EndIf
  ProcedureReturn #NO
EndProcedure


OpenWindow(0, 0, 0, 420, 320, "Terminate request example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
EditorGadget(0, 10, 10, 400, 300)

Repeat
  Event = WaitWindowEvent()
  If Event = #Event_TerminateRequested
    AddGadgetItem(0, -1, "Terminate requested at:" + FormatDate("%hh:%ii:%ss", Date()))
  EndIf
Until Event = #PB_Event_CloseWindow