1. hi World Clock GUI (win32lib) program
- Posted by ron77 3 weeks ago
- 180 views
Hi all, it's been a while since my last post - so I downloaded and installed Euphoria again (this time I've switched back to my Windows 11 OS) and also win32lib, and I decided to try and code "something useful" It's a GUI app for Windows using Win32Lib in Euphoria. It's a world clock app that shows the local time. You can add cities from a list of 250 cities around the world. It will show you the time there as well as the timezone - you select a city and click add city. It saves it in an ini file for next time you run the program and you can remove a city by clicking it (fast) and pressing del button or just press remove city and it will ask you if to remove the last added town from the list... worldclock.exw
-- World Clock Application in Euphoria using win32lib -- Port from C/Windows API version include win32lib.ew include std/io.e include std/get.e include std/datetime.e -- Constants constant MAX_CITIES = 250 constant MAX_SELECTED_CITIES = 20 constant INI_FILE_NAME = "worldclock.ini" -- Control IDs constant ID_COMBO_CITIES = 1001 constant ID_BUTTON_ADD = 1002 constant ID_LISTBOX_CLOCKS = 1003 constant ID_TIMER = 1004 constant ID_BUTTON_REMOVE = 1005 -- Window and control variables atom hMainWnd, hCombo, hButton, hListBox, hRemoveButton integer selected_count, city_count -- City structure: {name, timezone, utc_offset_hours, utc_offset_minutes} sequence cities, selected_cities -- Initialize cities database procedure init_cities() cities = { -- North America {"New York", "EST/EDT", -5, 0}, {"Los Angeles", "PST/PDT", -8, 0}, {"Chicago", "CST/CDT", -6, 0}, {"Denver", "MST/MDT", -7, 0}, {"Miami", "EST/EDT", -5, 0}, {"Seattle", "PST/PDT", -8, 0}, {"Toronto", "EST/EDT", -5, 0}, {"Vancouver", "PST/PDT", -8, 0}, {"Montreal", "EST/EDT", -5, 0}, {"Mexico City", "CST", -6, 0}, {"Anchorage", "AKST/AKDT", -9, 0}, {"Honolulu", "HST", -10, 0}, {"Phoenix", "MST", -7, 0}, {"Las Vegas", "PST/PDT", -8, 0}, {"Atlanta", "EST/EDT", -5, 0}, {"Boston", "EST/EDT", -5, 0}, {"Dallas", "CST/CDT", -6, 0}, {"Houston", "CST/CDT", -6, 0}, {"Philadelphia", "EST/EDT", -5, 0}, {"San Francisco", "PST/PDT", -8, 0}, {"Washington DC", "EST/EDT", -5, 0}, {"Detroit", "EST/EDT", -5, 0}, {"Minneapolis", "CST/CDT", -6, 0}, {"New Orleans", "CST/CDT", -6, 0}, {"Portland", "PST/PDT", -8, 0}, {"San Diego", "PST/PDT", -8, 0}, {"Orlando", "EST/EDT", -5, 0}, {"Nashville", "CST/CDT", -6, 0}, {"Memphis", "CST/CDT", -6, 0}, {"Cleveland", "EST/EDT", -5, 0}, -- Europe {"London", "GMT/BST", 0, 0}, {"Paris", "CET/CEST", 1, 0}, {"Berlin", "CET/CEST", 1, 0}, {"Rome", "CET/CEST", 1, 0}, {"Madrid", "CET/CEST", 1, 0}, {"Amsterdam", "CET/CEST", 1, 0}, {"Vienna", "CET/CEST", 1, 0}, {"Brussels", "CET/CEST", 1, 0}, {"Zurich", "CET/CEST", 1, 0}, {"Munich", "CET/CEST", 1, 0}, {"Frankfurt", "CET/CEST", 1, 0}, {"Milan", "CET/CEST", 1, 0}, {"Barcelona", "CET/CEST", 1, 0}, {"Copenhagen", "CET/CEST", 1, 0}, {"Stockholm", "CET/CEST", 1, 0}, {"Oslo", "CET/CEST", 1, 0}, {"Helsinki", "EET/EEST", 2, 0}, {"Warsaw", "CET/CEST", 1, 0}, {"Prague", "CET/CEST", 1, 0}, {"Budapest", "CET/CEST", 1, 0}, {"Athens", "EET/EEST", 2, 0}, {"Lisbon", "WET/WEST", 0, 0}, {"Dublin", "GMT/IST", 0, 0}, {"Edinburgh", "GMT/BST", 0, 0}, {"Moscow", "MSK", 3, 0}, {"Istanbul", "TRT", 3, 0}, {"Kiev", "EET/EEST", 2, 0}, {"St Petersburg", "MSK", 3, 0}, {"Reykjavik", "GMT", 0, 0}, {"Luxembourg", "CET/CEST", 1, 0}, -- Asia {"Tokyo", "JST", 9, 0}, {"Beijing", "CST", 8, 0}, {"Shanghai", "CST", 8, 0}, {"Hong Kong", "HKT", 8, 0}, {"Singapore", "SGT", 8, 0}, {"Seoul", "KST", 9, 0}, {"Mumbai", "IST", 5, 30}, {"Delhi", "IST", 5, 30}, {"Bangalore", "IST", 5, 30}, {"Chennai", "IST", 5, 30}, {"Kolkata", "IST", 5, 30}, {"Bangkok", "ICT", 7, 0}, {"Jakarta", "WIB", 7, 0}, {"Manila", "PHT", 8, 0}, {"Kuala Lumpur", "MYT", 8, 0}, {"Ho Chi Minh City", "ICT", 7, 0}, {"Taipei", "CST", 8, 0}, {"Dhaka", "BST", 6, 0}, {"Karachi", "PKT", 5, 0}, {"Lahore", "PKT", 5, 0}, {"Islamabad", "PKT", 5, 0}, {"Kabul", "AFT", 4, 30}, {"Tehran", "IRST", 3, 30}, {"Baghdad", "AST", 3, 0}, {"Riyadh", "AST", 3, 0}, {"Dubai", "GST", 4, 0}, {"Kuwait City", "AST", 3, 0}, {"Doha", "AST", 3, 0}, {"Abu Dhabi", "GST", 4, 0}, {"Muscat", "GST", 4, 0}, {"Yerevan", "AMT", 4, 0}, {"Baku", "AZT", 4, 0}, {"Tbilisi", "GET", 4, 0}, {"Almaty", "ALMT", 6, 0}, {"Tashkent", "UZT", 5, 0}, {"Bishkek", "KGT", 6, 0}, {"Dushanbe", "TJT", 5, 0}, {"Ashgabat", "TMT", 5, 0}, {"Ulaanbaatar", "ULAT", 8, 0}, {"Colombo", "SLST", 5, 30}, -- Africa {"Cairo", "EET", 2, 0}, {"Lagos", "WAT", 1, 0}, {"Casablanca", "WET", 0, 0}, {"Johannesburg", "SAST", 2, 0}, {"Cape Town", "SAST", 2, 0}, {"Nairobi", "EAT", 3, 0}, {"Addis Ababa", "EAT", 3, 0}, {"Dar es Salaam", "EAT", 3, 0}, {"Kampala", "EAT", 3, 0}, {"Kigali", "CAT", 2, 0}, {"Accra", "GMT", 0, 0}, {"Abidjan", "GMT", 0, 0}, {"Dakar", "GMT", 0, 0}, {"Tunis", "CET", 1, 0}, {"Algiers", "CET", 1, 0}, {"Tripoli", "EET", 2, 0}, {"Khartoum", "CAT", 2, 0}, {"Kinshasa", "WAT", 1, 0}, {"Luanda", "WAT", 1, 0}, {"Maputo", "CAT", 2, 0}, {"Harare", "CAT", 2, 0}, {"Lusaka", "CAT", 2, 0}, {"Gaborone", "CAT", 2, 0}, {"Windhoek", "CAT", 2, 0}, {"Antananarivo", "EAT", 3, 0}, {"Port Louis", "MUT", 4, 0}, {"Rabat", "WET", 0, 0}, {"Alexandria", "EET", 2, 0}, {"Durban", "SAST", 2, 0}, {"Pretoria", "SAST", 2, 0}, -- Oceania {"Sydney", "AEST/AEDT", 10, 0}, {"Melbourne", "AEST/AEDT", 10, 0}, {"Brisbane", "AEST", 10, 0}, {"Perth", "AWST", 8, 0}, {"Adelaide", "ACST/ACDT", 9, 30}, {"Auckland", "NZST/NZDT", 12, 0}, {"Wellington", "NZST/NZDT", 12, 0}, {"Christchurch", "NZST/NZDT", 12, 0}, {"Canberra", "AEST/AEDT", 10, 0}, {"Darwin", "ACST", 9, 30}, {"Hobart", "AEST/AEDT", 10, 0}, {"Gold Coast", "AEST", 10, 0}, {"Cairns", "AEST", 10, 0}, {"Fiji", "FJT", 12, 0}, {"Samoa", "WSST", 13, 0}, {"Tonga", "TOT", 13, 0}, {"Vanuatu", "VUT", 11, 0}, {"Papua New Guinea", "PGT", 10, 0}, {"New Caledonia", "NCT", 11, 0}, {"Solomon Islands", "SBT", 11, 0}, -- South America {"Sao Paulo", "BRT", -3, 0}, {"Rio de Janeiro", "BRT", -3, 0}, {"Buenos Aires", "ART", -3, 0}, {"Lima", "PET", -5, 0}, {"Bogota", "COT", -5, 0}, {"Santiago", "CLT", -4, 0}, {"Caracas", "VET", -4, 0}, {"Quito", "ECT", -5, 0}, {"La Paz", "BOT", -4, 0}, {"Asuncion", "PYT", -3, 0}, {"Montevideo", "UYT", -3, 0}, {"Georgetown", "GYT", -4, 0}, {"Paramaribo", "SRT", -3, 0}, {"Cayenne", "GFT", -3, 0}, {"Brasilia", "BRT", -3, 0}, {"Salvador", "BRT", -3, 0}, {"Fortaleza", "BRT", -3, 0}, {"Recife", "BRT", -3, 0}, {"Belo Horizonte", "BRT", -3, 0}, {"Porto Alegre", "BRT", -3, 0}, {"Manaus", "AMT", -4, 0}, {"Curitiba", "BRT", -3, 0}, {"Goiania", "BRT", -3, 0}, {"Belem", "BRT", -3, 0}, {"Medellin", "COT", -5, 0}, {"Cali", "COT", -5, 0}, {"Barranquilla", "COT", -5, 0}, {"Cordoba", "ART", -3, 0}, {"Rosario", "ART", -3, 0}, {"Mendoza", "ART", -3, 0}, -- Central America & Caribbean {"Guatemala City", "CST", -6, 0}, {"San Jose", "CST", -6, 0}, {"Panama City", "EST", -5, 0}, {"Managua", "CST", -6, 0}, {"Tegucigalpa", "CST", -6, 0}, {"San Salvador", "CST", -6, 0}, {"Belize City", "CST", -6, 0}, {"Kingston", "EST", -5, 0}, {"Havana", "CST/CDT", -5, 0}, {"Santo Domingo", "AST", -4, 0}, {"Port-au-Prince", "EST/EDT", -5, 0}, {"San Juan", "AST", -4, 0}, {"Bridgetown", "AST", -4, 0}, {"Port of Spain", "AST", -4, 0}, {"Nassau", "EST/EDT", -5, 0}, {"George Town", "EST", -5, 0}, {"Hamilton", "AST/ADT", -4, 0}, {"Road Town", "AST", -4, 0}, {"Charlotte Amalie", "AST", -4, 0}, {"Willemstad", "AST", -4, 0}, -- Additional Asia-Pacific {"Kathmandu", "NPT", 5, 45}, {"Thimphu", "BTT", 6, 0}, {"Yangon", "MMT", 6, 30}, {"Phnom Penh", "ICT", 7, 0}, {"Vientiane", "ICT", 7, 0}, {"Bandar Seri Begawan", "BNT", 8, 0}, {"Dili", "TLT", 9, 0}, {"Male", "MVT", 5, 0}, {"Vladivostok", "VLAT", 10, 0}, {"Irkutsk", "IRKT", 8, 0}, {"Novosibirsk", "NOVT", 7, 0}, {"Yekaterinburg", "YEKT", 5, 0}, {"Omsk", "OMST", 6, 0}, {"Krasnoyarsk", "KRAT", 7, 0}, {"Yakutsk", "YAKT", 9, 0}, {"Magadan", "MAGT", 11, 0}, {"Petropavlovsk", "PETT", 12, 0}, {"Anadyr", "ANAT", 12, 0}, {"Pyongyang", "KST", 9, 0}, {"Osaka", "JST", 9, 0}, {"Kyoto", "JST", 9, 0}, {"Yokohama", "JST", 9, 0}, {"Nagoya", "JST", 9, 0}, {"Sapporo", "JST", 9, 0}, {"Fukuoka", "JST", 9, 0}, {"Busan", "KST", 9, 0}, {"Incheon", "KST", 9, 0}, {"Daegu", "KST", 9, 0}, {"Daejeon", "KST", 9, 0}, {"Gwangju", "KST", 9, 0}, {"Chengdu", "CST", 8, 0}, {"Guangzhou", "CST", 8, 0}, {"Shenzhen", "CST", 8, 0}, {"Tianjin", "CST", 8, 0}, {"Wuhan", "CST", 8, 0}, {"Xi'an", "CST", 8, 0}, {"Nanjing", "CST", 8, 0}, {"Chongqing", "CST", 8, 0}, {"Hangzhou", "CST", 8, 0}, {"Qingdao", "CST", 8, 0}, {"Macau", "CST", 8, 0}, {"Jerusalem", "IST", 2, 0}, {"Tel Aviv", "IST", 2, 0}, {"Haifa", "IST", 2, 0}, {"Amman", "EET/EEST", 2, 0}, {"Beirut", "EET/EEST", 2, 0}, {"Damascus", "EET/EEST", 2, 0}, {"Nicosia", "EET/EEST", 2, 0}, {"Valletta", "CET/CEST", 1, 0}, {"Monaco", "CET/CEST", 1, 0} } city_count = length(cities) selected_cities = {} selected_count = 0 end procedure -- Get current date/time as sequence {year, month, day, hour, minute, second} function get_current_datetime() sequence dt = now() -- now() returns {year, month, day, hour, minute, second, day_of_week, day_of_year} -- We only need the first 6 elements return dt[1..6] end function -- Calculate time for a city based on UTC offset function get_city_time(sequence city) sequence local_time = get_current_datetime() atom utc_offset_hours = city[3] atom utc_offset_minutes = city[4] -- Get local time components atom year = local_time[1] atom month = local_time[2] atom day = local_time[3] atom hour = local_time[4] atom minute = local_time[5] atom second = local_time[6] -- Add the UTC offset to get city time -- This is simplified - doesn't account for local timezone or DST hour += utc_offset_hours minute += utc_offset_minutes -- Handle minute overflow if minute >= 60 then minute -= 60 hour += 1 elsif minute < 0 then minute += 60 hour -= 1 end if -- Handle hour overflow/underflow if hour >= 24 then hour -= 24 elsif hour < 0 then hour += 24 end if return {hour, minute, second} end function -- Format time difference function format_time_difference(atom diff_minutes) atom hours, minutes sequence buffer hours = floor(abs(diff_minutes) / 60) minutes = remainder(abs(diff_minutes), 60) if diff_minutes = 0 then buffer = "Same time" elsif diff_minutes > 0 then if minutes = 0 then buffer = sprintf("+%d hours ahead", hours) else buffer = sprintf("+%d:%02d hours ahead", {hours, minutes}) end if else if minutes = 0 then buffer = sprintf("-%d hours behind", hours) else buffer = sprintf("-%d:%02d hours behind", {hours, minutes}) end if end if return buffer end function -- Calculate time difference between local and city time function calculate_time_difference(sequence city) -- Simplified calculation - just return the UTC offset difference return (city[3] * 60 + city[4]) end function -- Update the clock display procedure update_clocks() sequence local_time, city_time, display_text, diff_str atom diff -- Clear the listbox properly while getCount(hListBox) > 0 do deleteItem(hListBox, 1) end while -- Add local time local_time = get_current_datetime() display_text = sprintf("LOCAL TIME: %02d:%02d:%02d", {local_time[4], local_time[5], local_time[6]}) addItem(hListBox, display_text) addItem(hListBox, "") -- Add selected cities for i = 1 to selected_count do city_time = get_city_time(selected_cities[i]) diff = calculate_time_difference(selected_cities[i]) diff_str = format_time_difference(diff) display_text = sprintf("%s (%s): %02d:%02d:%02d - %s", {selected_cities[i][1], selected_cities[i][2], city_time[1], city_time[2], city_time[3], diff_str}) addItem(hListBox, display_text) end for end procedure -- Save selected cities to INI file procedure save_selected_cities() integer file_handle sequence content file_handle = open(INI_FILE_NAME, "w") if file_handle = -1 then return end if printf(file_handle, "[SelectedCities]\n") printf(file_handle, "Count=%d\n", selected_count) for i = 1 to selected_count do printf(file_handle, "City%d=%s\n", {i-1, selected_cities[i][1]}) end for close(file_handle) end procedure -- Load selected cities from INI file procedure load_selected_cities() integer file_handle sequence file_content, lines object temp_line -- Initialize selected_cities = {} selected_count = 0 -- Try to open file file_handle = open(INI_FILE_NAME, "r") if file_handle = -1 then -- File doesn't exist, that's OK return end if -- Read entire file file_content = "" while 1 do temp_line = gets(file_handle) if atom(temp_line) then exit end if file_content &= temp_line end while close(file_handle) -- If file is empty, return if length(file_content) = 0 then return end if -- Split into lines lines = {} sequence current_line = "" for i = 1 to length(file_content) do if file_content[i] = '\n' then if length(current_line) > 0 then lines = append(lines, current_line) end if current_line = "" elsif file_content[i] != '\r' then current_line &= file_content[i] end if end for -- Add last line if it doesn't end with newline if length(current_line) > 0 then lines = append(lines, current_line) end if -- Process each line for i = 1 to length(lines) do sequence line = lines[i] -- Skip empty lines and section headers if length(line) = 0 or line[1] = '[' then -- Skip this line elsif length(line) > 5 and equal(line[1..4], "City") then -- Look for equals sign integer eq_pos = 0 for j = 5 to length(line) do if line[j] = '=' then eq_pos = j exit end if end for -- Extract city name if equals found if eq_pos > 0 and eq_pos < length(line) then sequence city_name = line[eq_pos+1..length(line)] -- Find city in database for k = 1 to city_count do if equal(cities[k][1], city_name) then if selected_count < MAX_SELECTED_CITIES then selected_count += 1 selected_cities = append(selected_cities, cities[k]) end if exit end if end for end if end if end for end procedure -- Add selected city procedure add_city() integer sel sequence city_name if selected_count >= MAX_SELECTED_CITIES then {} = message_box("Maximum number of cities reached!", "Warning", MB_OK) return end if sel = getIndex(hCombo) if sel = 0 then {} = message_box("Please select a city!", "Warning", MB_OK) return end if -- Check if city already added city_name = cities[sel][1] for i = 1 to selected_count do if equal(selected_cities[i][1], city_name) then {} = message_box("City already added!", "Warning", MB_OK) return end if end for -- Add the city selected_count += 1 selected_cities = append(selected_cities, cities[sel]) -- Save to INI file save_selected_cities() -- Update display update_clocks() end procedure -- Remove selected city procedure remove_selected_city() integer sel, city_index sequence message_text -- Get the selection sel = getIndex(hListBox) -- Debug: print the selection printf(1, "Selected index: %d\n", sel) if sel = 0 or sel = -1 then -- No selection, we'll handle this in the button handler return end if if sel <= 2 then -- Skip local time and empty line {} = message_box("Cannot remove local time entry!\n\nPlease select a city (not the local time).", "Information", MB_OK) return end if city_index = sel - 2 printf(1, "City index: %d, selected_count: %d\n", {city_index, selected_count}) if city_index >= 1 and city_index <= selected_count then message_text = sprintf("Remove %s from the list?", {selected_cities[city_index][1]}) if message_box(message_text, "Confirm Removal", MB_YESNO) = IDYES then -- Remove city from array if city_index = 1 and selected_count = 1 then -- Only one city, make array empty selected_cities = {} elsif city_index = selected_count then -- Last city, remove it selected_cities = selected_cities[1..selected_count-1] else -- City in middle, join before and after selected_cities = selected_cities[1..city_index-1] & selected_cities[city_index+1..selected_count] end if selected_count -= 1 -- Save the updated list save_selected_cities() -- Update display update_clocks() printf(1, "City removed successfully. New count: %d\n", selected_count) end if else printf(1, "Invalid city index: %d\n", city_index) {} = message_box("Invalid selection!", "Error", MB_OK) end if end procedure -- Timer procedure procedure timer_handler(integer id, integer event, sequence params) if event = w32HTimer then update_clocks() end if end procedure -- Alternative: Remove the last added city procedure remove_last_city() sequence message_text if selected_count = 0 then {} = message_box("No cities to remove!", "Information", MB_OK) return end if message_text = sprintf("Remove %s (last city) from the list?", {selected_cities[selected_count][1]}) if message_box(message_text, "Confirm Removal", MB_YESNO) = IDYES then selected_count -= 1 if selected_count = 0 then selected_cities = {} else selected_cities = selected_cities[1..selected_count] end if -- Save the updated list save_selected_cities() -- Update display update_clocks() printf(1, "Last city removed successfully. New count: %d\n", selected_count) end if end procedure -- Button handlers procedure button_add_handler(integer id, integer event, sequence params) if event = w32HClick then add_city() end if end procedure procedure button_remove_handler(integer id, integer event, sequence params) if event = w32HClick then -- Try normal remove first remove_selected_city() -- If that didn't work (selection = 0), offer to remove last city if getIndex(hListBox) = 0 and selected_count > 0 then remove_last_city() end if end if end procedure -- Event handler for main window procedure main_window_handler(integer id, integer event, sequence params) if event = w32HResize then -- Handle window resizing sequence rect = getClientRect(hMainWnd) integer width = rect[3] - rect[1] integer height = rect[4] - rect[2] -- Resize controls setRect(hCombo, 10, 10, width - 200, 25, 1) setRect(hButton, width - 180, 10, 80, 25, 1) setRect(hRemoveButton, width - 90, 10, 80, 25, 1) setRect(hListBox, 10, 50, width - 20, height - 60, 1) end if end procedure -- Listbox event handler for Delete key procedure listbox_handler(integer id, integer event, sequence params) if event = w32HKeyDown and params[1] = VK_DELETE then remove_selected_city() end if end procedure -- Main program procedure main() -- Initialize cities database init_cities() -- Create main window hMainWnd = create(Window, "World Clock Application", 0, 50, 50, 600, 500, {WS_OVERLAPPEDWINDOW, WS_VISIBLE}) setHandler(hMainWnd, w32HActivate, routine_id("main_window_handler")) setHandler(hMainWnd, w32HResize, routine_id("main_window_handler")) -- Create combo box hCombo = create(ComboBox, "", hMainWnd, 10, 10, 350, 200, CBS_DROPDOWNLIST) -- Populate combo box with cities for i = 1 to city_count do addItem(hCombo, cities[i][1]) end for -- Create buttons hButton = create(PushButton, "Add City", hMainWnd, 370, 10, 80, 25, 0) setHandler(hButton, w32HClick, routine_id("button_add_handler")) hRemoveButton = create(PushButton, "Remove", hMainWnd, 460, 10, 80, 25, 0) setHandler(hRemoveButton, w32HClick, routine_id("button_remove_handler")) -- Create listbox - make sure it's visible hListBox = create(ListBox, "", hMainWnd, 10, 45, 560, 400, 0) setHandler(hListBox, w32HKeyDown, routine_id("listbox_handler")) -- Load previously selected cities load_selected_cities() -- Set up timer for updating clocks every second setTimer(hMainWnd, ID_TIMER, 1000) setHandler(hMainWnd, w32HTimer, routine_id("timer_handler")) -- Initial clock update update_clocks() -- Debug: Print what we have loaded printf(1, "Selected cities count: %d\n", selected_count) for i = 1 to selected_count do printf(1, "City %d: %s\n", {i, selected_cities[i][1]}) end for -- Force a window refresh repaintWindow(hMainWnd) -- Start the message loop WinMain(hMainWnd, Normal) end procedure -- Start the application main()